diskimg 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/bin/bash
  2. # This script allows backuping a complete SD card, NAND or eMMC card using tar utility
  3. #
  4. _resolve_file_location(){
  5. SOURCE="$1"
  6. while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  7. THE_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  8. SOURCE="$(readlink "$SOURCE")"
  9. [[ "$SOURCE" != "/*" ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  10. done
  11. THE_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  12. echo $THE_DIR
  13. }
  14. _get_relative_path_to(){
  15. source=$1
  16. target=$2
  17. common_part=$source
  18. back=
  19. while [ "${target#$common_part}" = "${target}" ]; do
  20. common_part=$(dirname $common_part)
  21. back="../${back}"
  22. done
  23. echo ${back}${target#$common_part/}
  24. }
  25. printHelp(){
  26. cat << EOF
  27. NAME:
  28. diskimg -- Creates or restores a partition
  29. SYNOPSIS:
  30. $0 -c TARGET_DIR -d DISK_DEVICE [-v]
  31. $0 -s SOURCE_DIR -d DISK_DEVICE [-v]
  32. DESCRIPTION:
  33. Creates a backup of the given device using the `-c` option. All partition
  34. table, boot sector, partition contents are stored into the given target
  35. directory in different files.
  36. Restores the backup using the `-r` option to the given device. All partition
  37. table, boot sector, partition contents are restored from the given target
  38. directory
  39. OPTIONS:
  40. -c creates the backup using the given TARGET_DIR value
  41. -d the DISK_DEVICE from/to process
  42. -e exclude file patterns from tar (creation). Re-use for several values (e.g -e /foo/bar -e /my/path)
  43. -z uses gzip compression
  44. -j uses bzip2 compression
  45. -P keep existing partitions
  46. -r restores from the data stored into the SOURCE_DIR value
  47. -J uses XZ compression (DEFAULT)
  48. -T skip tar archive creation (useful if backup only partition layout)
  49. -v Verbose
  50. -D dry run
  51. EXAMPLES:
  52. One can easily recreate the partition scheme using using the following command
  53. (which will not write the data from the tar files) :
  54. $0 -r /my/path/to/backup -d /dev/sdb -T
  55. Then it is possible to change the layout (resize filesystem to partition size e.g.)
  56. And then to restore the data (option -P will preserve existing partitions):
  57. $0 -r /my/path/to/backup -d /dev/sdb -P
  58. EOF
  59. exit 0
  60. }
  61. OPTS=$(getopt c:d:e:DhjJPr:Tvz $*)
  62. #Test les paramètres
  63. if [ $? != 0 ]
  64. then
  65. echo "Error while retrieving paramger data from getopt"
  66. printHelp
  67. exit 1
  68. fi
  69. eval set -- "$OPTS"
  70. DISK_DEVICE=""
  71. TARGET_DIR=""
  72. SOURCE_DIR=""
  73. COMPRESSION_ALGORITHM="-J"
  74. COMPRESSION_SUFFIX="xz"
  75. VERBOSE_OPTION=""
  76. EXCLUDE_PATTERNS=""
  77. KEEP_PARTITIONS=""
  78. while true ; do
  79. case "$1" in
  80. -d) DISK_DEVICE="$2" ; shift ; shift
  81. ;;
  82. -c) TARGET_DIR="$2" ; shift ; shift
  83. ;;
  84. -e) EXCLUDE_PATTERNS="$EXCLUDE_PATTERNS $2" ; shift ; shift
  85. ;;
  86. -r) SOURCE_DIR="$2" ; shift ; shift
  87. ;;
  88. -z) COMPRESSION_ALGORITHM="-z" ; COMPRESSION_SUFFIX="gz" ; shift
  89. ;;
  90. -j) COMPRESSION_ALGORITHM="-j"; COMPRESSION_SUFFIX="bzip2" ; shift
  91. ;;
  92. -v) VERBOSE="1"; VERBOSE_OPTION="-v" ; shift
  93. ;;
  94. -D) DEBUG="1"; shift
  95. ;;
  96. -P) KEEP_PARTITIONS="1"; shift
  97. ;;
  98. -T) SKIP_TAR="1"; shift
  99. ;;
  100. -h|--help)
  101. printHelp
  102. exit 0
  103. ;;
  104. --) shift; break
  105. ;;
  106. esac
  107. done
  108. create_exclude_string(){
  109. EXCLUDE_OPTION="--exclude=."
  110. EXCLUDE_STRING=""
  111. for pattern in $(echo $EXCLUDE_PATTERNS) ; do
  112. EXCLUDE_STRING="$EXCLUDE_STRING $EXCLUDE_OPTION$pattern"
  113. done
  114. }
  115. do_restore_disk(){
  116. echo "Restoring disk image from $SOURCE_DIR to $DISK_DEVICE"
  117. if [[ "$KEEP_PARTITIONS" != "1" ]] ; then
  118. echo "Restoring boot sector backup"
  119. test "1" != "$DEBUG" && dcfldd if=$SOURCE_DIR/boot-sector.img of=$DISK_DEVICE || exit 44
  120. echo "Restoring partition table backup"
  121. test "1" != "$DEBUG" && sfdisk $DISK_DEVICE < $SOURCE_DIR/partition-table.sfdisk || exit 45
  122. echo "Updating from partition information"
  123. test "1" != "$DEBUG" && hdparm -z $DISK_DEVICE
  124. fi
  125. ls $SOURCE_DIR/partition*.tar.$COMPRESSION_SUFFIX > /dev/null 2>&1
  126. if [[ "$?" != "0" ]] ; then
  127. echo "There are no files with the suffix .tar.$COMPRESSION_SUFFIX in directory $SOURCE_DIR"
  128. echo "Please check you are using the right compression algorithm (options could be -j -J or -z)"
  129. exit 6
  130. fi
  131. #For each partition, we restore the backup
  132. for partition_file in $(ls $SOURCE_DIR/partition*.tar.$COMPRESSION_SUFFIX) ; do
  133. partition_dev=$(basename $partition_file | sed "s+partition-+$DISK_DEVICE+" |sed "s/\.tar\.$COMPRESSION_SUFFIX//")
  134. backup_basename=$(basename $partition_file)
  135. backup_filename="$backup_basename.tar.$COMPRESSION_SUFFIX "
  136. partition_type_file=$SOURCE_DIR/$(echo $backup_basename | sed "s+\.tar\.$COMPRESSION_SUFFIX+.type+")
  137. echo ""
  138. echo "--> Restoring backup $partition_file into $partition_dev"
  139. if [[ "$KEEP_PARTITIONS" != "1" ]] ; then
  140. #First get the partition type
  141. partition_type=$(cat $partition_type_file)
  142. echo "Formating $partition_dev with type $partition_type"
  143. test "1" != "$DEBUG" && mkfs -t $partition_type $partition_dev
  144. test "$?" == 0 || exit $?
  145. fi
  146. mount_dir_name="diskimg-$backup_basename"
  147. mountpoint_dir="/mnt/$mount_dir_name"
  148. test "1" = "$VERBOSE" && echo "Mounting $partition_dev to $mountpoint_dir"
  149. mkdir -p $mountpoint_dir
  150. test "$?" == 0 || exit $?
  151. mount $partition_dev $mountpoint_dir
  152. test "$?" == 0 || exit $?
  153. test "1" = "$VERBOSE" && echo "Restoring from $partition_file to $mountpoint_dir"
  154. if [[ "$SKIP_TAR" != 1 ]] ; then
  155. test "1" = "$DEBUG" && echo tar --numeric-owner $COMPRESSION_ALGORITHM $VERBOSE_OPTION -pxf $partition_file -C $mountpoint_dir
  156. test "1" != "$DEBUG" && tar --numeric-owner $COMPRESSION_ALGORITHM $VERBOSE_OPTION -pxf $partition_file -C $mountpoint_dir
  157. fi
  158. test "$?" == 0 || exit $?
  159. test "1" = "$VERBOSE" && echo "Un-mounting $partition_dev"
  160. umount $partition_dev
  161. test "$?" == 0 || exit $?
  162. rmdir $mountpoint_dir
  163. test "$?" == 0 || exit $?
  164. done
  165. }
  166. restore_disk(){
  167. if [[ -d "$SOURCE_DIR" ]] ; then
  168. echo "Restoring disk image from $SOURCE_DIR to $DISK_DEVICE"
  169. echo ""
  170. echo "You will lose any data stored on $DISK_DEVICE"
  171. echo "Please make sure you have a backup"
  172. echo ""
  173. echo "ARE YOU SURE you want to erase any data on $DISK_DEVICE ? (yes/NO)"
  174. read answer
  175. while [[ "$answer" =~ [yY]$ ]] ; do
  176. echo "Please type yes or YES"
  177. read answer
  178. done
  179. if [[ "$answer" =~ yes ]] ; then
  180. do_restore_disk
  181. fi
  182. else
  183. echo "ERROR: $SOURCE_DIR does not exist"
  184. exit 4
  185. fi
  186. }
  187. backup_disk(){
  188. if [[ ! -d "$TARGET_DIR" ]] ; then
  189. echo "$TARGET_DIR does not exist"
  190. echo "Do you want to create it ? (y/N)"
  191. read answer
  192. if [[ "$answer" =~ [yY] ]] ; then
  193. mkdir -p $TARGET_DIR
  194. else
  195. echo "Aborting because target dir does not exist"
  196. exit 126
  197. fi
  198. fi
  199. if [[ -d "$TARGET_DIR" ]] ; then
  200. echo "Creating disk image from $DISK_DEVICE to $TARGET_DIR..."
  201. echo "Creating boot sector backup"
  202. test "1" != "$DEBUG" && echo dcfldd if=$DISK_DEVICE bs=1M count=1 of=$TARGET_DIR/boot-sector.img
  203. test "1" != "$DEBUG" && dcfldd if=$DISK_DEVICE bs=1M count=1 of=$TARGET_DIR/boot-sector.img
  204. echo "Creating partition table backup"
  205. test "1" != "$DEBUG" && echo sfdisk -d $DISK_DEVICE > $TARGET_DIR/partition-table.sfdisk
  206. test "1" != "$DEBUG" && sfdisk -d $DISK_DEVICE > $TARGET_DIR/partition-table.sfdisk
  207. create_exclude_string
  208. #For each partition, we make a backup
  209. for partition_dev in $(sfdisk -d $DISK_DEVICE | grep -F 'start=' | awk '{print $1}') ; do
  210. backup_basename=$(echo $partition_dev | sed "s+$DISK_DEVICE+partition-+")
  211. #Partition type
  212. echo "Saving partition type"
  213. part_type_filename="$backup_basename.type"
  214. lsblk -no FSTYPE $partition_dev > $TARGET_DIR/$part_type_filename
  215. backup_filename="$backup_basename.tar.$COMPRESSION_SUFFIX "
  216. echo "Creating backup for $partition_dev into $backup_filename"
  217. mount_dir_name="diskimg-$backup_basename"
  218. mountpoint_dir="/mnt/$mount_dir_name"
  219. test "1" = "$VERBOSE" && echo "Mounting $partition_dev to $mountpoint_dir"
  220. mkdir -p $mountpoint_dir
  221. mount $partition_dev $mountpoint_dir
  222. test "1" = "$VERBOSE" && echo "Backing up from $mountpoint_dir to $TARGET_DIR/$backup_filename"
  223. if [[ "$SKIP_TAR" != 1 ]] ; then
  224. test "1" = "$DEBUG" && echo "tar --numeric-owner $COMPRESSION_ALGORITHM $VERBOSE_OPTION $EXCLUDE_STRING -pcf $TARGET_DIR/$backup_filename -C $mountpoint_dir ."
  225. tar --numeric-owner $COMPRESSION_ALGORITHM $VERBOSE_OPTION $EXCLUDE_STRING -pcf $TARGET_DIR/$backup_filename -C $mountpoint_dir .
  226. fi
  227. umount $partition_dev
  228. rmdir $mountpoint_dir
  229. done
  230. fi
  231. }
  232. if [ -z "$TARGET_DIR" ] && [ -z "$SOURCE_DIR" ]
  233. then
  234. echo "You must indicate an operation with -c or -r"
  235. exit 2
  236. fi
  237. if [ -z "$DISK_DEVICE" ]
  238. then
  239. echo "You must indicate a disk device"
  240. exit 3
  241. fi
  242. if [[ $EUID -ne 0 ]]; then
  243. echo "This script cannot work if not super-user. Please run as root" 1>&2
  244. exit 1
  245. fi
  246. if [[ "$TARGET_DIR" != "" ]] ; then
  247. backup_disk
  248. exit
  249. fi
  250. if [[ "$SOURCE_DIR" != "" ]] ; then
  251. restore_disk
  252. exit
  253. fi