Add a USB based card reader, insert the SD card you want to work on:
Use the script:
https://github.com/KoljaWindeler/pi-clone
Afterwards you can access the cloned SD card to make more alterations.
#!/bin/bash PGM=`basename $0` RSYNC_OPTIONS="--force -rltWDEgopt" #-r recursive #-l copy symlinks as symlinks #-t preserve modification time #-W copy whole files #-D #-E preserve file executability #-g preserve group #-o preserve owner #-p preerve permission #-t preserver times # List of extra dirs to create under /mnt. OPTIONAL_MNT_DIRS="" # Where to mount the disk filesystems to be rsynced. DEST_ROOT_PATH=/mnt/clone SRC_ROOT_PATH=/mnt/source CLONE_LOG=/var/log/$PGM.log HOSTNAME=`hostname` # e.g. mmcblk0->p<-1, we dont need that in sdb-><-1 ... PARTITION_SPACER="p" # check if we are root if [ `id -u` != 0 ] then echo -e "$PGM needs to be run as root.\n" exit 1 fi # check if we have rsync installed if ! rsync --version > /dev/null then echo -e "\nOoops! $PGM needs the rsync program but cannot run it." echo "Make sure rsync is installed:" echo " $ sudo apt-get update" echo -e " $ sudo apt-get install rsync\n" exit 0 fi if test -e /sbin/fsck.vfat then HAVE_FSCK_VFAT="yes" else echo "[Note] fsck.vfat was not found." echo "It is recommended to install dosfstools:" echo " $ sudo apt-get update" echo " $ sudo apt-get install dosfstools" fi usage() { echo "" echo "usage: $PGM sdN sdM {-f|--force-initialize} {-v|--verbose} {-y|--yes-to-all}" echo " Example: $PGM sda" echo " to clone the current running system from /dev/mmcblk0" echo " Example: $PGM sda sdb " echo " to restore the system from /dev/sda to /dev/sdb" echo " -v - list all files as they are copied." echo " -f - force initialize the destination partitions" echo " -y - skip all questions" echo "" echo " $PGM can run in two ways" echo " 1) $PGM sdN (without sdM) will cloning your current running system" echo " to a SD card installed on sdN" echo " 2) $PGM sdN sdM will clone the system on disk sdN to the disk sdM" echo "" echo " $PGM can clone the system to a new SD card or can" echo " incrementally rsync to existing backup Raspberry/Banana Pi SD cards." echo "" echo " If the destination SD card has an existing $SRC_BOOT_PARTITION_TYPE partition 1 and a" echo " $SRC_ROOT_PARTITION_TYPE partition 2, $PGM assumes (unless using the -f option)" echo " that the SD card is an existing backup with the partitions" echo " properly sized and set up for a Raspberry/Banana Pi. All that is needed" echo " is to mount the partitions and rsync them to the source system." echo "" echo " If these partitions are not found (or -f), then $PGM will ask" echo " if it is OK to initialize the destination SD card partitions." echo " This is done by a partial 'dd' from the source device /dev/mmcblk0" echo " or /dev/sdN to the destination SD card followed by a" echo " fdisk resize and mkfs.ext4 of partition 2." echo " This creates a completed $SRC_BOOT_PARTITION_TYPE partition 1 containing all boot" echo " files and an empty but properly sized partition 2 rootfs." echo " The SD card partitions are then mounted and rsynced to the" echo " running system." echo "" echo " The SD card destination partitions will be mounted on $DEST_ROOT_PATH." echo " A log will be written to $CLONE_LOG." echo " Avoid running other disk writing programs when running $PGM." echo "" exit 0 } VERBOSE=off RESTORE_MODE=0 NO_REQUEST=false while [ "$1" ] do case "$1" in -v|--verbose) VERBOSE=on RSYNC_OPTIONS=${RSYNC_OPTIONS}v ;; -f|--force-initialize) FORCE_INITIALIZE=true ;; -h|--help) usage ;; -y|--yes-to-all) NO_REQUEST=true ;; *) if [ "$DST_DISK" != "" ] then if [ "$SRC_DISK" != "" ] then echo "Bad args" usage fi SRC_DISK=$DST_DISK RESTORE_MODE=1 PARTITION_SPACER="" fi DST_DISK=$1 ;; esac shift done if [ "$SRC_DISK" = "" ] then SRC_DISK=mmcblk0 fi SRC_BOOT_PARTITION_TYPE=`parted /dev/$SRC_DISK -ms p | grep "^1" | cut -f 5 -d:` SRC_ROOT_PARTITION_TYPE=`parted /dev/$SRC_DISK -ms p | grep "^2" | cut -f 5 -d:` if [ "$DST_DISK" = "" ] then usage exit 0 fi if ! cat /proc/partitions | grep -q $DST_DISK then echo "Destination disk '$DST_DISK' does not exist." echo "Plug the destination SD card into a USB port." echo "If it does not show up as '$DST_DISK', then do a" echo -e "'cat /proc/partitions' to see where it might be.\n" exit 0 fi if [ "$RESTORE_MODE" = "1" ] then if ! cat /proc/partitions | grep -q $SRC_DISK then echo "Source disk '$SRC_DISK' does not exist." echo "Plug the destination SD card into a USB port." echo "If it does not show up as '$SRC_DISK', then do a" echo -e "'cat /proc/partitions' to see where it might be.\n" exit 0 fi fi unmount_or_abort() { echo -n "Do you want to unmount $1? (yes/no): " if [ "$NO_REQUEST" = "true" ] then resp="y" echo "yes" else read resp fi if [ "$resp" = "y" ] || [ "$resp" = "yes" ] then if ! umount $1 then echo "Sorry, $PGM could not unmount $1." echo -e "Aborting!\n" exit 0 fi else echo -e "Aborting!\n" exit 0 fi } DST_ROOT_PARTITION=/dev/${DST_DISK}2 DST_BOOT_PARTITION=/dev/${DST_DISK}1 SRC_ROOT_PARTITION=/dev/${SRC_DISK}${PARTITION_SPACER}2 SRC_BOOT_PARTITION=/dev/${SRC_DISK}${PARTITION_SPACER}1 # Check that none of the destination partitions are busy (mounted). # src partitions can be mounted as we are just reading from them # DST_ROOT_CURMOUNT=`fgrep "$DST_ROOT_PARTITION " /etc/mtab | cut -f 2 -d ' ' ` DST_BOOT_CURMOUNT=`fgrep "$DST_BOOT_PARTITION " /etc/mtab | cut -f 2 -d ' ' ` if [ "$DST_ROOT_CURMOUNT" != "" ] || [ "$DST_BOOT_CURMOUNT" != "" ] then echo "A destination partition is busy (mounted). Mount status:" echo " $DST_ROOT_PARTITION: $DST_ROOT_CURMOUNT" echo " $DST_BOOT_PARTITION: $DST_BOOT_CURMOUNT" if [ "$DST_BOOT_CURMOUNT" != "" ] then unmount_or_abort $DST_BOOT_CURMOUNT fi if [ "$DST_ROOT_CURMOUNT" != "" ] then unmount_or_abort $DST_ROOT_CURMOUNT fi fi # check that no other drive is mounted on our mountpoint for destination TEST_MOUNTED=`fgrep " $DEST_ROOT_PATH " /etc/mtab | cut -f 1 -d ' ' ` if [ "$TEST_MOUNTED" != "" ] then echo "This script uses $DEST_ROOT_PATH for mounting filesystems, but" echo "$DEST_ROOT_PATH is already mounted with $TEST_MOUNTED." unmount_or_abort $DEST_ROOT_PATH fi # check that no other drive is mounted on our mountpoint for source if [ "$RESTORE_MODE" = "1" ] then TEST_MOUNTED=`fgrep " $SRC_ROOT_PATH/boot " /etc/mtab | cut -f 1 -d ' ' ` if [ "$TEST_MOUNTED" != "" ] then echo "This script uses $SRC_ROOT_PATH/boot for mounting filesystems, but" echo "$SRC_ROOT_PATH/boot is already mounted with $TEST_MOUNTED." unmount_or_abort $SRC_ROOT_PATH"/boot" fi TEST_MOUNTED=`fgrep " $SRC_ROOT_PATH " /etc/mtab | cut -f 1 -d ' ' ` if [ "$TEST_MOUNTED" != "" ] then echo "This script uses $SRC_ROOT_PATH for mounting filesystems, but" echo "$SRC_ROOT_PATH is already mounted with $TEST_MOUNTED." unmount_or_abort $SRC_ROOT_PATH fi fi # check that /mnt is not mounted on another drive if [ ! -d $DEST_ROOT_PATH ] then MNT_MOUNT=`fgrep " /mnt " /etc/mtab | cut -f 1 -d ' ' ` if [ "$MNT_MOUNT" != "" ] then echo "$MNT_MOUNT is currently mounted on /mnt." unmount_or_abort /mnt fi mkdir $DEST_ROOT_PATH fi # ensure source mount dir is available if [ "$RESTORE_MODE" = "1" ] then if [ ! -d $SRC_ROOT_PATH ] then mkdir $SRC_ROOT_PATH fi fi # Borrowed from do_expand_rootfs in raspi-config expand_rootfs() { # Get the starting offset of the root partition PART_START=$(parted /dev/$SRC_DISK -ms unit s p | grep "^2" | cut -f 2 -d:) [ "$PART_START" ] || return 1 # Return value will likely be error for fdisk as it fails to reload the # partition table because the root fs is mounted fdisk /dev/$DST_DISK > /dev/null <<EOF p d 2 n p 2 $PART_START p w q EOF } # =========== Disk Setup and Checks =========== # # Check that destination partitions are the right type. # DST_BOOT_PARTITION_TYPE=`parted /dev/$DST_DISK -ms p \ | grep "^1" | cut -f 5 -d:` DST_ROOT_PARTITION_TYPE=`parted /dev/$DST_DISK -ms p \ | grep "^2" | cut -f 5 -d:` SRC_BOOT_PARTITION_SIZE=`parted /dev/$SRC_DISK -ms p \ | grep "^1" | cut -f 3 -d:` DST_BOOT_PARTITION_SIZE=`parted /dev/$DST_DISK -ms p \ | grep "^1" | cut -f 3 -d:` if [ "$DST_BOOT_PARTITION_TYPE" != "$SRC_BOOT_PARTITION_TYPE" ] || \ [ "$DST_ROOT_PARTITION_TYPE" != "$SRC_ROOT_PARTITION_TYPE" ] || \ [ "$SRC_BOOT_PARTITION_SIZE" != "$DST_BOOT_PARTITION_SIZE" ] || \ [ "$FORCE_INITIALIZE" = "true" ] then echo "" if [ "$FORCE_INITIALIZE" = "true" ] then echo "*** Forcing a partition initialization of destination '$DST_DISK' ***" fi echo "The existing partitions on destination disk '$DST_DISK' are:" # fdisk -l /dev/$DST_DISK | grep $DST_DISK parted /dev/$DST_DISK unit MB p \ | sed "/^Model/d ; /^Sector/d" if [ "$DST_BOOT_PARTITION_TYPE" != "$SRC_BOOT_PARTITION_TYPE" ] then echo " ... Cannot find a destination boot file system of type: $SRC_BOOT_PARTITION_TYPE" echo "" fi if [ "$DST_ROOT_PARTITION_TYPE" != "$SRC_ROOT_PARTITION_TYPE" ] then echo " ... Cannot find a destination root file system of type: $SRC_ROOT_PARTITION_TYPE" echo "" fi if [ "$SRC_BOOT_PARTITION_SIZE" != "$DST_BOOT_PARTITION_SIZE" ] then echo " ... The boot partitions have different sizes." echo " Required $SRC_BOOT_PARTITION_SIZE vs $DST_BOOT_PARTITION_SIZE available on $DST_DISK" echo "" fi echo "This script can initialize the destination disk with a partition" echo "structure copied from the source filesytem and then resize" echo "partition 2 (the root filesystem) to use all space on the SD card." echo -n "Do you want to initialize the destination /dev/$DST_DISK? (yes/no): " if [ "$NO_REQUEST" = "true" ] then resp="y" echo "yes" else read resp fi if [ "$resp" = "y" ] || [ "$resp" = "yes" ] then # Image onto the destination disk a beginning fragment of the # running SD card file structure that spans at least more than # the start of partition 2. # # Calculate the start of partition 2 in MB for the dd. PART2_START=$(parted /dev/$SRC_DISK -ms unit MB p | grep "^2" \ | cut -f 2 -d: | sed s/MB// | tr "," "." | cut -f 1 -d.) # and add some slop DD_COUNT=`expr $PART2_START + 8` echo "" echo "Imaging the partition structure, copying $DD_COUNT megabytes..." sync dd if=/dev/$SRC_DISK of=/dev/$DST_DISK bs=1M count=$DD_COUNT # Partition was copied live so fsck to clean up for possible future # "Volume was not properly unmounted" warnings. if [ "$HAVE_FSCK_VFAT" = "yes" ] then echo "Running fsck on $DST_BOOT_PARTITION..." fsck -p $DST_BOOT_PARTITION &> /dev/null fi # But, though Partion 1 is now imaged, partition 2 is incomplete and # maybe the wrong size for the destination SD card. So fdisk it to # make it fill the rest of the disk and mkfs it to clean it out. # echo "Sizing partition 2 (root partition) to use all SD card space..." expand_rootfs mkfs.ext4 $DST_ROOT_PARTITION > /dev/null echo "" echo "/dev/$DST_DISK is initialized and resized. Its partitions are:" # fdisk -l /dev/$DST_DISK | grep $DST_DISK parted /dev/$DST_DISK unit MB p \ | sed "/^Model/d ; /^Sector/d" SRC_ROOT_VOL_NAME=`e2label /dev/${SRC_DISK}${PARTITION_SPACER}2` echo "" echo "Your booted /dev/${SRC_DISK}${PARTITION_SPACER}2 rootfs existing label: $SRC_ROOT_VOL_NAME" echo -n "You may enter a label for the destination rootfs $DST_ROOT_PARTITION: " if [ "$NO_REQUEST" = "true" ] then resp="" echo "skipped" else read resp fi if [ "$resp" != "" ] then e2label $DST_ROOT_PARTITION $resp fi else echo -e "Aborting\n" exit 0 fi fi # =========== Setup Summary =========== # DST_ROOT_VOL_NAME=`e2label $DST_ROOT_PARTITION` SRC_ROOT_VOL_NAME=`e2label $SRC_ROOT_PARTITION` if [ "$DST_ROOT_VOL_NAME" = "" ] then DST_ROOT_VOL_NAME="no label" fi if [ "$SRC_ROOT_VOL_NAME" = "" ] then SRC_ROOT_VOL_NAME="no label" fi echo "" if [ "$RESTORE_MODE" = "1" ] then echo "Restore mode : yes" echo "Source disk : $SRC_DISK" echo "Source bootfs : $SRC_BOOT_PARTITION on ${SRC_ROOT_PATH}/boot" echo "Source rootfs : $SRC_ROOT_PARTITION ($SRC_ROOT_VOL_NAME) on ${SRC_ROOT_PATH}" else echo "Restore mode : no" fi echo "Clone destination disk : $DST_DISK" echo "Clone destination bootfs : $DST_BOOT_PARTITION on ${DEST_ROOT_PATH}/boot" echo "Clone destination rootfs : $DST_ROOT_PARTITION ($DST_ROOT_VOL_NAME) on ${DEST_ROOT_PATH}" echo "Verbose mode : $VERBOSE" echo "===============================" # If this is an SD card initialization, can watch progress of the clone # in another terminal with: watch df -h # echo -n "Final check, is it Ok to proceed with the clone (yes/no)?: " if [ "$NO_REQUEST" = "true" ] then resp="y" echo "yes" else read resp fi if [ "$resp" != "y" ] && [ "$resp" != "yes" ] then echo -e "Aborting the disk clone.\n" exit 0 fi # # =========== End of Setup =========== # mount the root filesystem if we are using the restore mode if [ "$RESTORE_MODE" = "1" ] then echo "=> Mounting /dev/${SRC_DISK}${PARTITION_SPACER}2 ($SRC_ROOT_VOL_NAME) on ${SRC_ROOT_PATH}" if ! mount /dev/${SRC_DISK}${PARTITION_SPACER}2 ${SRC_ROOT_PATH} then echo -e "Mount failure on ${SRC_ROOT_PATH}, aborting!\n" exit 0 fi fi # ensure that the boot partition is mounted before running rsync SRC_BOOT_PATH="/boot" if [ "$RESTORE_MODE" = "1" ] then SRC_BOOT_PATH="${SRC_ROOT_PATH}/boot" if [ ! -d ${BOOT_PATH} ] then mkdir ${BOOT_PATH} fi fi BOOT_MOUNT=`fgrep " ${SRC_BOOT_PATH} " /etc/mtab | cut -f 1 -d ' ' ` if [ "$BOOT_MOUNT" = "" ] then echo "=> Mounting /dev/${SRC_DISK}${PARTITION_SPACER}1 on ${SRC_BOOT_PATH}" if [ ! -d ${SRC_BOOT_PATH} ] then mkdir -p ${SRC_BOOT_PATH} fi if ! mount /dev/${SRC_DISK}${PARTITION_SPACER}1 ${SRC_BOOT_PATH} then echo -e "Mount failure on ${SRC_BOOT_PATH}, aborting!\n" exit 0 fi fi # Mount destination filesystems. echo "=> Mounting $DST_ROOT_PARTITION ($DST_ROOT_VOL_NAME) on $DEST_ROOT_PATH" if ! mount $DST_ROOT_PARTITION $DEST_ROOT_PATH then echo -e "Mount failure of $DST_ROOT_PARTITION, aborting!\n" exit 0 fi if [ ! -d $DEST_ROOT_PATH/boot ] then mkdir -p $DEST_ROOT_PATH/boot fi echo "=> Mounting $DST_BOOT_PARTITION on $DEST_ROOT_PATH/boot" if ! mount $DST_BOOT_PARTITION $DEST_ROOT_PATH/boot then umount $DEST_ROOT_PATH echo -e "Mount failure of $DST_BOOT_PARTITION, aborting!\n" exit 0 fi echo "===============================" START_TIME=`date '+%H:%M:%S'` # Exclude fuse mountpoint .gvfs, various other mount points, and tmpfs # file systems from the rsync. # sync RSYNC_SRC_PATH="/" if [ "$RESTORE_MODE" = "1" ] then RSYNC_SRC_PATH="${SRC_ROOT_PATH}/" fi echo "Starting the filesystem rsync ${RSYNC_SRC_PATH} to $DEST_ROOT_PATH" echo -n "(This may take several minutes)..." rsync $RSYNC_OPTIONS --delete \ --exclude '.gvfs' \ --exclude ${RSYNC_SRC_PATH}'dev' \ --exclude ${RSYNC_SRC_PATH}'media' \ --exclude ${RSYNC_SRC_PATH}'mnt' \ --exclude ${RSYNC_SRC_PATH}'proc' \ --exclude ${RSYNC_SRC_PATH}'run' \ --exclude ${RSYNC_SRC_PATH}'sys' \ --exclude ${RSYNC_SRC_PATH}'tmp' \ --exclude ${RSYNC_SRC_PATH}'lost\+found' \ $RSYNC_SRC_PATH \ $DEST_ROOT_PATH # Fixup some stuff # for i in dev media mnt proc run sys do if [ ! -d $DEST_ROOT_PATH/$i ] then mkdir $DEST_ROOT_PATH/$i fi done if [ ! -d $DEST_ROOT_PATH/tmp ] then mkdir $DEST_ROOT_PATH/tmp chmod a+w $DEST_ROOT_PATH/tmp fi # Some extra optional dirs I create under /mnt for i in $OPTIONAL_MNT_DIRS do if [ ! -d $DEST_ROOT_PATH/mnt/$i ] then mkdir $DEST_ROOT_PATH/mnt/$i fi done #rm -f $DEST_ROOT_PATH/etc/udev/rules.d/70-persistent-net.rules DATE=`date '+%F %H:%M'` echo "$DATE $HOSTNAME $PGM : clone to $DST_DISK ($DST_ROOT_VOL_NAME)" \ >> $CLONE_LOG echo "$DATE $HOSTNAME $PGM : clone to $DST_DISK ($DST_ROOT_VOL_NAME)" \ >> $DEST_ROOT_PATH/$CLONE_LOG STOP_TIME=`date '+%H:%M:%S'` echo "" echo "*** Done with clone to /dev/$DST_DISK ***" echo " Started: $START_TIME Finished: $STOP_TIME" echo "" # Pause before unmounting in case I want to inspect the clone results # or need to custom modify any files on the destination SD clone. # Eg. modify $DEST_ROOT_PATH/etc/hostname, $DEST_ROOT_PATH/etc/network/interfaces, etc # if I'm cloning into a card to be installed on another Pi. # echo -n "Hit Enter when ready to unmount the used partitions..." if [ "$NO_REQUEST" = "true" ] then echo "skipped" else read resp fi echo "unmounting $DEST_ROOT_PATH/boot" umount $DEST_ROOT_PATH/boot echo "unmounting $DEST_ROOT_PATH" umount $DEST_ROOT_PATH if [ "$RESTORE_MODE" = "1" ] then echo "unmounting $SRC_ROOT_PATH/boot" umount $SRC_ROOT_PATH/boot echo "unmounting $SRC_ROOT_PATH" umount $SRC_ROOT_PATH fi echo "===============================" exit 0
Related posts:
Thin client/Remote desktop client RPi
Raspberry Pi Thin Client project Thin Client project want to create a very low price thin client over Raspberry Pi bo...
Raspberry Pi Thin Client project Thin Client project want to create a very low price thin client over Raspberry Pi bo...
DietPi
I am going to have a long and serious look at DietPi the next month! Very impressive collection of OS and applicati...
I am going to have a long and serious look at DietPi the next month! Very impressive collection of OS and applicati...
CP/M on the Pi! Ultibo baremetal app
EMUZ80 RPI is a CP/M emulator, by Ronald Daleske, for the Z80 processor the Raspberry PI (2). Here is the emulator witho...
EMUZ80 RPI is a CP/M emulator, by Ronald Daleske, for the Z80 processor the Raspberry PI (2). Here is the emulator witho...
Tiny LCD DSPI based screens
A recent test of the tiny LCD screens (2.8 to 3.5 inch) I have, to assess if they support a recent version of Raspbian....
A recent test of the tiny LCD screens (2.8 to 3.5 inch) I have, to assess if they support a recent version of Raspbian....