mkimage.sh.in 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #!/bin/sh
  2. #-
  3. # Copyright (c) 2013-2016 Juan Romero Pardines.
  4. # Copyright (c) 2017 Google
  5. # All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions
  9. # are met:
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in the
  14. # documentation and/or other materials provided with the distribution.
  15. #
  16. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  17. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  18. # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  19. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  20. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  21. # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  25. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. #-
  27. readonly PROGNAME=$(basename "$0")
  28. readonly ARCH=$(uname -m)
  29. trap 'printf "\nInterrupted! exiting...\n"; cleanup; exit 0' INT TERM HUP
  30. # This source pulls in all the functions from lib.sh. This set of
  31. # functions makes it much easier to work with chroots and abstracts
  32. # away all the problems with running binaries with QEMU.
  33. # shellcheck source=./lib.sh
  34. . ./lib.sh
  35. cleanup() {
  36. unmount_pseudofs
  37. umount -f "${ROOTFS}/boot" 2>/dev/null
  38. umount -f "${ROOTFS}" 2>/dev/null
  39. if [ -e "$LOOPDEV" ]; then
  40. partx -d "$LOOPDEV" 2>/dev/null
  41. losetup -d "$LOOPDEV" 2>/dev/null
  42. fi
  43. [ -d "$ROOTFS" ] && rmdir "$ROOTFS"
  44. }
  45. usage() {
  46. cat <<_EOF
  47. Usage: $PROGNAME [options] <rootfs-tarball>
  48. The <rootfs-tarball> argument expects a tarball generated by void-mkrootfs.
  49. The platform is guessed automatically by its name.
  50. Accepted sizes suffixes: KiB, MiB, GiB, TiB, EiB.
  51. OPTIONS
  52. -b <fstype> Set /boot filesystem type (defaults to FAT)
  53. -B <bsize> Set /boot filesystem size (defaults to 64MiB)
  54. -r <fstype> Set / filesystem type (defaults to EXT4)
  55. -s <totalsize> Set total image size (defaults to 2GB)
  56. -o <output> Set image filename (guessed automatically)
  57. -h Show this help
  58. -V Show version
  59. Resulting image will have 2 partitions, /boot and /.
  60. _EOF
  61. exit 0
  62. }
  63. # ########################################
  64. # SCRIPT EXECUTION STARTS HERE
  65. # ########################################
  66. while getopts "b:B:o:r:s:hV" opt; do
  67. case $opt in
  68. b) BOOT_FSTYPE="$OPTARG";;
  69. B) BOOT_FSSIZE="$OPTARG";;
  70. o) FILENAME="$OPTARG";;
  71. r) ROOT_FSTYPE="$OPTARG";;
  72. s) IMGSIZE="$OPTARG";;
  73. V) echo "$PROGNAME @@MKLIVE_VERSION@@"; exit 0;;
  74. h) usage;;
  75. esac
  76. done
  77. shift $((OPTIND - 1))
  78. ROOTFS_TARBALL="$1"
  79. if [ -z "$ROOTFS_TARBALL" ]; then
  80. usage
  81. elif [ ! -r "$ROOTFS_TARBALL" ]; then
  82. # In rare cases the tarball can wind up owned by the wrong user.
  83. # This leads to confusing failures if execution is allowed to
  84. # proceed.
  85. die "Cannot read rootfs tarball: $ROOTFS_TARBALL"
  86. fi
  87. # By default we build all platform images with a 64MiB boot partition
  88. # formated FAT16, and an approxomately 1.9GiB root partition formated
  89. # ext4. More exotic combinations are of course possible, but this
  90. # combination works on all known platforms.
  91. : "${IMGSIZE:=2G}"
  92. : "${BOOT_FSTYPE:=vfat}"
  93. : "${BOOT_FSSIZE:=64MiB}"
  94. : "${ROOT_FSTYPE:=ext4}"
  95. # Verify that the required tooling is available
  96. readonly REQTOOLS="sfdisk partx losetup mount truncate mkfs.${BOOT_FSTYPE} mkfs.${ROOT_FSTYPE}"
  97. check_tools
  98. # Setup the platform variable. Here we want just the name and
  99. # optionally -musl if this is the musl variant.
  100. PLATFORM="${ROOTFS_TARBALL#void-}"
  101. PLATFORM="${PLATFORM%-ROOTFS*}"
  102. # This is an aweful hack since the script isn't using privesc
  103. # mechanisms selectively. This is a TODO item.
  104. if [ "$(id -u)" -ne 0 ]; then
  105. die "need root perms to continue, exiting."
  106. fi
  107. # Set the default filename if none was provided above. The default
  108. # will include the platform the image is being built for and the date
  109. # on which it was built.
  110. if [ -z "$FILENAME" ]; then
  111. FILENAME="void-${PLATFORM}-$(date +%Y%m%d).img"
  112. fi
  113. # Be absolutely certain the platform is supported before continuing
  114. case "$PLATFORM" in
  115. bananapi|beaglebone|cubieboard2|cubietruck|odroid-c2|odroid-u2|rpi|rpi2|rpi3|usbarmory|GCP|*-musl);;
  116. *) die "The $PLATFORM is not supported, exiting..."
  117. esac
  118. # Create the base image. This was previously accomplished with dd,
  119. # but truncate is markedly faster.
  120. info_msg "Creating disk image ($IMGSIZE) ..."
  121. truncate -s "${IMGSIZE}" "$FILENAME" >/dev/null 2>&1
  122. # Grab a tmpdir for the rootfs. If this fails we need to halt now
  123. # because otherwise things will go very badly for the host system.
  124. ROOTFS=$(mktemp -d) || die "Could not create tmpdir for ROOTFS"
  125. info_msg "Creating disk image partitions/filesystems ..."
  126. if [ "$BOOT_FSTYPE" = "vfat" ]; then
  127. _args="-I -F16"
  128. fi
  129. case "$PLATFORM" in
  130. cubieboard2|cubietruck|ci20*|odroid-c2*)
  131. sfdisk "${FILENAME}" <<_EOF
  132. label: dos
  133. 2048,,L
  134. _EOF
  135. LOOPDEV=$(losetup --show --find --partscan "$FILENAME")
  136. mkfs.${ROOT_FSTYPE} -O '^64bit,^extra_isize,^has_journal' "${LOOPDEV}p1" >/dev/null 2>&1
  137. mount "${LOOPDEV}p1" "$ROOTFS"
  138. ROOT_UUID=$(blkid -o value -s UUID "${LOOPDEV}p1")
  139. ;;
  140. *)
  141. sfdisk "${FILENAME}" <<_EOF
  142. label: dos
  143. 2048,${BOOT_FSSIZE},b,*
  144. ,+,L
  145. _EOF
  146. LOOPDEV=$(losetup --show --find --partscan "$FILENAME")
  147. # Normally we need to quote to prevent argument splitting, but
  148. # we explicitly want argument splitting here.
  149. # shellcheck disable=SC2086
  150. mkfs.${BOOT_FSTYPE} $_args "${LOOPDEV}p1" >/dev/null
  151. case "$ROOT_FSTYPE" in
  152. ext[34]) disable_journal="-O ^has_journal";;
  153. esac
  154. mkfs.${ROOT_FSTYPE} "$disable_journal" "${LOOPDEV}p2" >/dev/null 2>&1
  155. mount "${LOOPDEV}p2" "$ROOTFS"
  156. mkdir -p "${ROOTFS}/boot"
  157. mount "${LOOPDEV}p1" "${ROOTFS}/boot"
  158. BOOT_UUID=$(blkid -o value -s UUID "${LOOPDEV}p1")
  159. ROOT_UUID=$(blkid -o value -s UUID "${LOOPDEV}p2")
  160. ;;
  161. esac
  162. info_msg "Unpacking rootfs tarball ..."
  163. if [ "$PLATFORM" = "beaglebone" ]; then
  164. fstab_args=",noauto"
  165. tar xfp "$ROOTFS_TARBALL" -C "$ROOTFS" ./boot/MLO
  166. tar xfp "$ROOTFS_TARBALL" -C "$ROOTFS" ./boot/u-boot.img
  167. touch "$ROOTFS/boot/uEnv.txt"
  168. umount "$ROOTFS/boot"
  169. fi
  170. tar xfp "$ROOTFS_TARBALL" --xattrs --xattrs-include='*' -C "$ROOTFS"
  171. fspassno="1"
  172. if [ "$ROOT_FSTYPE" = "f2fs" ]; then
  173. fspassno="0"
  174. fi
  175. echo "UUID=$ROOT_UUID / $ROOT_FSTYPE defaults 0 ${fspassno}" >> "${ROOTFS}/etc/fstab"
  176. if [ -n "$BOOT_UUID" ]; then
  177. echo "UUID=$BOOT_UUID /boot $BOOT_FSTYPE defaults${fstab_args} 0 2" >> "${ROOTFS}/etc/fstab"
  178. fi
  179. info_msg "Configuring image for platform $PLATFORM"
  180. case "$PLATFORM" in
  181. bananapi*|cubieboard2*|cubietruck*)
  182. dd if="${ROOTFS}/boot/u-boot-sunxi-with-spl.bin" of="${LOOPDEV}" bs=1024 seek=8 >/dev/null 2>&1
  183. ;;
  184. odroid-c2*)
  185. dd if="${ROOTFS}/boot/bl1.bin.hardkernel" of="${LOOPDEV}" bs=1 count=442 >/dev/null 2>&1
  186. dd if="${ROOTFS}/boot/bl1.bin.hardkernel" of="${LOOPDEV}" bs=512 skip=1 seek=1 >/dev/null 2>&1
  187. dd if="${ROOTFS}/boot/u-boot.bin" of="${LOOPDEV}" bs=512 seek=97 >/dev/null 2>&1
  188. ;;
  189. odroid-u2*)
  190. dd if="${ROOTFS}/boot/E4412_S.bl1.HardKernel.bin" of="${LOOPDEV}" seek=1 >/dev/null 2>&1
  191. dd if="${ROOTFS}/boot/bl2.signed.bin" of="${LOOPDEV}" seek=31 >/dev/null 2>&1
  192. dd if="${ROOTFS}/boot/u-boot.bin" of="${LOOPDEV}" seek=63 >/dev/null 2>&1
  193. dd if="${ROOTFS}/boot/E4412_S.tzsw.signed.bin" of="${LOOPDEV}" seek=2111 >/dev/null 2>&1
  194. ;;
  195. usbarmory*)
  196. dd if="${ROOTFS}/boot/u-boot.imx" of="${LOOPDEV}" bs=512 seek=2 conv=fsync >/dev/null 2>&1
  197. ;;
  198. ci20*)
  199. dd if="${ROOTFS}/boot/u-boot-spl.bin" of="${LOOPDEV}" obs=512 seek=1 >/dev/null 2>&1
  200. dd if="${ROOTFS}/boot/u-boot.img" of="${LOOPDEV}" obs=1K seek=14 >/dev/null 2>&1
  201. ;;
  202. GCP*)
  203. # Setup GRUB
  204. mount_pseudofs
  205. chroot "${ROOTFS}" grub-install "${LOOPDEV}"
  206. sed -i "s:page_poison=1:page_poison=1 console=ttyS0,38400n8d:" "${ROOTFS}/etc/default/grub"
  207. chroot "${ROOTFS}" update-grub
  208. umount_pseudofs
  209. # Setup the GCP Guest services
  210. for _service in dhcpcd sshd agetty-console nanoklogd socklog-unix GCP-Guest-Initialization GCP-accounts GCP-clock-skew GCP-ip-forwarding ; do
  211. chroot "${ROOTFS}" ln -sv /etc/sv/$_service /etc/runit/runsvdir/default/$_service
  212. done
  213. # Turn off the agetty's since we can't use them anyway
  214. rm -v "${ROOTFS}/etc/runit/runsvdir/default/agetty-tty*"
  215. # Disable root login over ssh and lock account
  216. sed -i "s:PermitRootLogin yes:PermitRootLogin no:" "${ROOTFS}/etc/ssh/sshd_config"
  217. chroot "${ROOTFS}" passwd -l root
  218. # Set the Timezone
  219. chroot "${ROOTFS}" ln -svf /usr/share/zoneinfo/UTC /etc/localtime
  220. # Generate glibc-locales if necessary (this is a noop on musl)
  221. if [ "$PLATFORM" = GCP ] ; then
  222. chroot "${ROOTFS}" xbps-reconfigure -f glibc-locales
  223. fi
  224. # Remove SSH host keys (these will get rebuilt on first boot)
  225. rm -f "${ROOTFS}/etc/ssh/*key*"
  226. rm -f "${ROOTFS}/etc/ssh/moduli"
  227. # Force hte hostname since this isn't read from DHCP
  228. echo void-GCE > "${ROOTFS}/etc/hostname"
  229. ;;
  230. esac
  231. umount -R "$ROOTFS"
  232. losetup -d "$LOOPDEV"
  233. rmdir "$ROOTFS" || die "$ROOTFS not empty!"
  234. chmod 644 "$FILENAME"
  235. case "$PLATFORM" in
  236. GCP*)
  237. mv void-GCP*.img disk.raw
  238. info_msg "Compressing disk.raw"
  239. tar Sczf "${FILENAME%.img}.tar.gz" disk.raw
  240. rm disk.raw
  241. info_msg "Sucessfully created ${FILENAME%.img}.tar.gz image."
  242. ;;
  243. *)
  244. info_msg "Successfully created $FILENAME image."
  245. ;;
  246. esac