You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

qcow2_handling 7.1 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #!/bin/bash
  2. # QCOW2 Routines
  3. export CURRENT_IMAGE
  4. export CURRENT_MOUNTPOINT
  5. export NBD_DEV
  6. export MAP_BOOT_DEV
  7. export MAP_ROOT_DEV
  8. # set in build.sh
  9. # should be fairly enough for the beginning
  10. # overwrite here by uncommenting following lines
  11. # BASE_QCOW2_SIZE=12G
  12. # find and initialize free block device nodes
  13. init_nbd() {
  14. modprobe nbd max_part=16
  15. if [ -z "${NBD_DEV}" ]; then
  16. for x in /sys/class/block/nbd* ; do
  17. S=`cat $x/size`
  18. if [ "$S" == "0" ] ; then
  19. NBD_DEV=/dev/$(basename $x)
  20. MAP_BOOT_DEV=/dev/mapper/$(basename $x)p1
  21. MAP_ROOT_DEV=/dev/mapper/$(basename $x)p2
  22. break
  23. fi
  24. done
  25. fi
  26. }
  27. export -f init_nbd
  28. # connect image to block device
  29. connect_blkdev() {
  30. init_nbd
  31. qemu-nbd --discard=unmap -c $NBD_DEV "$1"
  32. sync
  33. kpartx -as $NBD_DEV
  34. sync
  35. CURRENT_IMAGE="$1"
  36. }
  37. export -f connect_blkdev
  38. # disconnect image from block device
  39. disconnect_blkdev() {
  40. kpartx -d $NBD_DEV
  41. qemu-nbd -d $NBD_DEV
  42. NBD_DEV=
  43. MAP_BOOT_DEV=
  44. MAP_ROOT_DEV=
  45. CURRENT_IMAGE=
  46. }
  47. export -f disconnect_blkdev
  48. # mount qcow2 image: mount_image <image file> <mountpoint>
  49. mount_qimage() {
  50. connect_blkdev "$1"
  51. mount -v -t ext4 $MAP_ROOT_DEV "$2"
  52. mkdir -p "${ROOTFS_DIR}/boot"
  53. mount -v -t vfat $MAP_BOOT_DEV "$2/boot"
  54. CURRENT_MOUNTPOINT="$2"
  55. }
  56. export -f mount_qimage
  57. # umount qcow2 image: umount_image <current mountpoint>
  58. umount_qimage() {
  59. sync
  60. #umount "$1/boot"
  61. while mount | grep -q "$1"; do
  62. local LOCS
  63. LOCS=$(mount | grep "$1" | cut -f 3 -d ' ' | sort -r)
  64. for loc in $LOCS; do
  65. echo "$loc"
  66. while mountpoint -q "$loc" && ! umount "$loc"; do
  67. sleep 0.1
  68. done
  69. done
  70. done
  71. CURRENT_MOUNTPOINT=
  72. disconnect_blkdev
  73. }
  74. export -f umount_qimage
  75. # create base image / backing image / mount image
  76. load_qimage() {
  77. if [ -z "${CURRENT_MOUNTPOINT}" ]; then
  78. if [ ! -d "${ROOTFS_DIR}" ]; then
  79. mkdir -p "${ROOTFS_DIR}";
  80. fi
  81. if [ "${CLEAN}" = "1" ] && [ -f "${WORK_DIR}/image-${STAGE}.qcow2" ]; then
  82. rm -f "${WORK_DIR}/image-${STAGE}.qcow2";
  83. fi
  84. if [ ! -f "${WORK_DIR}/image-${STAGE}.qcow2" ]; then
  85. pushd ${WORK_DIR} > /dev/null
  86. init_nbd
  87. if [ -z "${PREV_STAGE}" ]; then
  88. echo "Creating base image: image-${STAGE}.qcow2"
  89. # -o preallocation=falloc
  90. qemu-img create -f qcow2 image-${STAGE}.qcow2 $BASE_QCOW2_SIZE
  91. sync
  92. qemu-nbd --discard=unmap -c $NBD_DEV image-${STAGE}.qcow2
  93. sync
  94. sfdisk $NBD_DEV << EOF
  95. 4MiB,250MiB,c,*
  96. 254MiB,,83;
  97. EOF
  98. sync
  99. kpartx -as $NBD_DEV
  100. mkdosfs -n boot -F 32 -v $MAP_BOOT_DEV
  101. mkfs.ext4 -L rootfs -O "^huge_file,^metadata_csum,^64bit" $MAP_ROOT_DEV
  102. sync
  103. else
  104. if [ ! -f "${WORK_DIR}/image-${PREV_STAGE}.qcow2" ]; then
  105. exit 1;
  106. fi
  107. echo "Creating backing image: image-${STAGE}.qcow2 <- ${WORK_DIR}/image-${PREV_STAGE}.qcow2"
  108. qemu-img create -f qcow2 \
  109. -o backing_file=${WORK_DIR}/image-${PREV_STAGE}.qcow2 \
  110. ${WORK_DIR}/image-${STAGE}.qcow2
  111. sync
  112. qemu-nbd --discard=unmap -c $NBD_DEV image-${STAGE}.qcow2
  113. sync
  114. kpartx -as $NBD_DEV
  115. fi
  116. mount -v -t ext4 $MAP_ROOT_DEV "${ROOTFS_DIR}"
  117. mkdir -p "${ROOTFS_DIR}/boot"
  118. mount -v -t vfat $MAP_BOOT_DEV "${ROOTFS_DIR}/boot"
  119. CURRENT_IMAGE=${WORK_DIR}/image-${STAGE}.qcow2
  120. CURRENT_MOUNTPOINT=${ROOTFS_DIR}
  121. popd > /dev/null
  122. else
  123. mount_qimage "${WORK_DIR}/image-${STAGE}.qcow2" "${ROOTFS_DIR}"
  124. fi
  125. echo "Current image in use: ${CURRENT_IMAGE} (MP: ${CURRENT_MOUNTPOINT})"
  126. fi
  127. }
  128. export -f load_qimage
  129. # umount current image and refresh mount point env var
  130. unload_qimage() {
  131. if [ ! -z "${CURRENT_MOUNTPOINT}" ]; then
  132. fstrim -v "${CURRENT_MOUNTPOINT}" || true
  133. umount_qimage "${CURRENT_MOUNTPOINT}"
  134. fi
  135. }
  136. export -f unload_qimage
  137. # based on: https://github.com/SirLagz/RaspberryPi-ImgAutoSizer
  138. # helper function for make_bootable_image, do not call directly
  139. function resize_qcow2() {
  140. if [ -z "$CALL_FROM_MBI" ]; then
  141. echo "resize_qcow2: cannot be called directly, use make_bootable_image instead"
  142. return 1
  143. fi
  144. # ROOT_MARGIN=$((800*1024*1024))
  145. ROOT_MARGIN=$((1*1024*1024))
  146. PARTED_OUT=`parted -s -m "$NBD_DEV" unit B print`
  147. PART_NO=`echo "$PARTED_OUT" | grep ext4 | awk -F: ' { print $1 } '`
  148. PART_START=`echo "$PARTED_OUT" | grep ext4 | awk -F: ' { print substr($2,1,length($2)-1) } '`
  149. e2fsck -y -f $MAP_ROOT_DEV || true
  150. DATA_SIZE=`resize2fs -P $MAP_ROOT_DEV | awk -F': ' ' { print $2 } '`
  151. BLOCK_SIZE=$(dumpe2fs -h $MAP_ROOT_DEV | grep 'Block size' | awk -F': ' ' { print $2 }')
  152. BLOCK_SIZE=${BLOCK_SIZE// /}
  153. let DATA_SIZE=$DATA_SIZE+$ROOT_MARGIN/$BLOCK_SIZE
  154. resize2fs -p $MAP_ROOT_DEV $DATA_SIZE
  155. sleep 1
  156. let PART_NEW_SIZE=$DATA_SIZE*$BLOCK_SIZE
  157. let PART_NEW_END=$PART_START+$PART_NEW_SIZE
  158. ACT1=`parted -s "$NBD_DEV" rm 2`
  159. ACT2=`parted -s "$NBD_DEV" unit B mkpart primary $PART_START $PART_NEW_END`
  160. NEW_IMG_SIZE=`parted -s -m "$NBD_DEV" unit B print free | tail -1 | awk -F: ' { print substr($2,1,length($2)-1) } '`
  161. }
  162. export -f resize_qcow2
  163. # create raw img from qcow2: make_bootable_image <in.qcow2> <out.img>
  164. function make_bootable_image() {
  165. EXPORT_QCOW2="$1"
  166. EXPORT_IMAGE="$2"
  167. echo "Connect block device to source qcow2"
  168. connect_blkdev "${EXPORT_QCOW2}"
  169. echo "Resize fs and partition"
  170. CALL_FROM_MBI=1
  171. resize_qcow2
  172. sync
  173. CALL_FROM_MBI=
  174. echo "Disconnect block device"
  175. disconnect_blkdev
  176. if [ -z "$NEW_IMG_SIZE" ]; then
  177. echo "NEW_IMG_SIZE could not be calculated, cannot process image. Exit."
  178. exit 1
  179. fi
  180. echo "Shrinking qcow2 image"
  181. qemu-img resize --shrink "${EXPORT_QCOW2}" $NEW_IMG_SIZE
  182. sync
  183. echo "Convert qcow2 to raw image"
  184. qemu-img convert -f qcow2 -O raw "${EXPORT_QCOW2}" "${EXPORT_IMAGE}"
  185. sync
  186. echo "Get PARTUUIDs from image"
  187. IMGID="$(blkid -o value -s PTUUID "${EXPORT_IMAGE}")"
  188. BOOT_PARTUUID="${IMGID}-01"
  189. echo "Boot: $BOOT_PARTUUID"
  190. ROOT_PARTUUID="${IMGID}-02"
  191. echo "Root: $ROOT_PARTUUID"
  192. echo "Mount image"
  193. MOUNTROOT=${WORK_DIR}/tmpimage
  194. mkdir -p $MOUNTROOT
  195. MOUNTPT=$MOUNTROOT
  196. PARTITION=2
  197. mount "${EXPORT_IMAGE}" "$MOUNTPT" -o loop,offset=$[ `/sbin/sfdisk -d "${EXPORT_IMAGE}" | grep "start=" | head -n $PARTITION | tail -n1 | sed 's/.*start=[ ]*//' | sed 's/,.*//'` * 512 ],sizelimit=$[ `/sbin/sfdisk -d "${EXPORT_IMAGE}" | grep "start=" | head -n $PARTITION | tail -n1 | sed 's/.*size=[ ]*//' | sed 's/,.*//'` * 512 ] || exit 1
  198. MOUNTPT=$MOUNTROOT/boot
  199. PARTITION=1
  200. mount "${EXPORT_IMAGE}" "$MOUNTPT" -o loop,offset=$[ `/sbin/sfdisk -d "${EXPORT_IMAGE}" | grep "start=" | head -n $PARTITION | tail -n1 | sed 's/.*start=[ ]*//' | sed 's/,.*//'` * 512 ],sizelimit=$[ `/sbin/sfdisk -d "${EXPORT_IMAGE}" | grep "start=" | head -n $PARTITION | tail -n1 | sed 's/.*size=[ ]*//' | sed 's/,.*//'` * 512 ] || exit 1
  201. if [ ! -d "${MOUNTROOT}/root" ]; then
  202. echo "Image damaged or not mounted. Exit."
  203. exit 1
  204. fi
  205. echo "Setup PARTUUIDs"
  206. if [ ! -z "$BOOT_PARTUUID" ] && [ ! -z "$ROOT_PARTUUID" ]; then
  207. echo "Set UUIDs to make it bootable"
  208. sed -i "s/BOOTDEV/PARTUUID=${BOOT_PARTUUID}/" "${MOUNTROOT}/etc/fstab"
  209. sed -i "s/ROOTDEV/PARTUUID=${ROOT_PARTUUID}/" "${MOUNTROOT}/etc/fstab"
  210. sed -i "s/ROOTDEV/PARTUUID=${ROOT_PARTUUID}/" "${MOUNTROOT}/boot/cmdline.txt"
  211. fi
  212. echo "Umount image"
  213. sync
  214. umount "${MOUNTROOT}/boot" || exit 1
  215. umount "${MOUNTROOT}" || exit 1
  216. echo "Remove qcow2 export image"
  217. rm -f "${EXPORT_QCOW2}"
  218. }
  219. export -f make_bootable_image