build-image-qemu.sh 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #!/bin/sh
  2. set -e
  3. die() {
  4. echo "die: $*"
  5. exit 1
  6. }
  7. if [ "$(id -u)" != 0 ]; then
  8. exec sudo -E -- "$0" "$@" || die "this script needs to run as root"
  9. else
  10. : "${SUDO_UID:=0}" "${SUDO_GID:=0}"
  11. fi
  12. if [ "$(uname -s)" = "Darwin" ]; then
  13. export PATH="/usr/local/opt/e2fsprogs/bin:$PATH"
  14. export PATH="/usr/local/opt/e2fsprogs/sbin:$PATH"
  15. export PATH="/opt/homebrew/opt/e2fsprogs/bin:$PATH"
  16. export PATH="/opt/homebrew/opt/e2fsprogs/sbin:$PATH"
  17. fi
  18. SCRIPT_DIR="$(dirname "${0}")"
  19. # Prepend the toolchain qemu directory so we pick up QEMU from there
  20. PATH="$SCRIPT_DIR/../Toolchain/Local/qemu/bin:$PATH"
  21. # Also prepend the i686 toolchain directory because that's where most
  22. # people will have their QEMU binaries if they built them before the
  23. # directory was changed to Toolchain/Local/qemu.
  24. PATH="$SCRIPT_DIR/../Toolchain/Local/i686/bin:$PATH"
  25. # We depend on GNU coreutils du for the --apparent-size extension.
  26. # GNU coreutils is a build dependency.
  27. if type gdu > /dev/null 2>&1; then
  28. GNUDU="gdu"
  29. else
  30. GNUDU="du"
  31. fi
  32. disk_usage() {
  33. # shellcheck disable=SC2003
  34. expr "$(${GNUDU} -sk --apparent-size "$1" | cut -f1)"
  35. }
  36. inode_usage() {
  37. find "$1" | wc -l
  38. }
  39. INODE_SIZE=128
  40. INODE_COUNT=$(($(inode_usage "$SERENITY_SOURCE_DIR/Base") + $(inode_usage Root)))
  41. DISK_SIZE_BYTES=$((($(disk_usage "$SERENITY_SOURCE_DIR/Base") + $(disk_usage Root) + INODE_COUNT) * 1024))
  42. # Try to use heuristics to guess a good disk size and inode count.
  43. # The disk must notably fit:
  44. # * Data blocks (for both files and directories),
  45. # * Indirect/doubly indirect/triply indirect blocks,
  46. # * Inodes and block bitmaps for each block group,
  47. # * Plenty of extra free space and free inodes.
  48. DISK_SIZE_BYTES=$(((DISK_SIZE_BYTES + (INODE_COUNT * INODE_SIZE * 2)) * 3))
  49. INODE_COUNT=$((INODE_COUNT * 7))
  50. USE_EXISTING=0
  51. if [ -f _disk_image ]; then
  52. USE_EXISTING=1
  53. echo "checking existing image"
  54. result=0
  55. e2fsck -f -y _disk_image || result=$?
  56. if [ $result -ge 4 ]; then
  57. rm -f _disk_image
  58. USE_EXISTING=0
  59. echo "failed, not using existing image"
  60. else
  61. echo "done"
  62. fi
  63. fi
  64. if [ $USE_EXISTING -eq 1 ]; then
  65. OLD_DISK_SIZE_BYTES=$(wc -c < _disk_image)
  66. if [ $DISK_SIZE_BYTES -gt "$OLD_DISK_SIZE_BYTES" ]; then
  67. echo "resizing disk image..."
  68. qemu-img resize -f raw _disk_image $DISK_SIZE_BYTES || die "could not resize disk image"
  69. if ! resize2fs _disk_image; then
  70. rm -f _disk_image
  71. USE_EXISTING=0
  72. echo "failed, not using existing image"
  73. fi
  74. echo "done"
  75. fi
  76. fi
  77. if [ $USE_EXISTING -ne 1 ]; then
  78. printf "setting up disk image... "
  79. qemu-img create -q -f raw _disk_image $DISK_SIZE_BYTES || die "could not create disk image"
  80. chown "$SUDO_UID":"$SUDO_GID" _disk_image || die "could not adjust permissions on disk image"
  81. echo "done"
  82. printf "creating new filesystem... "
  83. if [ "$(uname -s)" = "OpenBSD" ]; then
  84. VND=$(vnconfig _disk_image)
  85. (echo "e 0"; echo 83; echo n; echo 0; echo "*"; echo "quit") | fdisk -e "$VND"
  86. newfs_ext2fs -D $INODE_SIZE -n $INODE_COUNT "/dev/r${VND}i" || die "could not create filesystem"
  87. else
  88. if [ -x /sbin/mke2fs ]; then
  89. /sbin/mke2fs -q -I $INODE_SIZE -N $INODE_COUNT _disk_image || die "could not create filesystem"
  90. else
  91. mke2fs -q -I $INODE_SIZE -N $INODE_COUNT _disk_image || die "could not create filesystem"
  92. fi
  93. fi
  94. echo "done"
  95. fi
  96. printf "mounting filesystem... "
  97. mkdir -p mnt
  98. use_genext2fs=0
  99. if [ "$(uname -s)" = "Darwin" ]; then
  100. mount_cmd="fuse-ext2 _disk_image mnt -o rw+,allow_other,uid=501,gid=20"
  101. elif [ "$(uname -s)" = "OpenBSD" ]; then
  102. VND=$(vnconfig _disk_image)
  103. mount_cmd="mount -t ext2fs "/dev/${VND}i" mnt/"
  104. elif [ "$(uname -s)" = "FreeBSD" ]; then
  105. MD=$(mdconfig _disk_image)
  106. mount_cmd="fuse-ext2 -o rw+,direct_io "/dev/${MD}" mnt/"
  107. else
  108. mount_cmd="mount _disk_image mnt/"
  109. fi
  110. if ! eval "$mount_cmd"; then
  111. if command -v genext2fs 1>/dev/null ; then
  112. echo "mount failed but genext2fs exists, use it instead"
  113. use_genext2fs=1
  114. else
  115. die "could not mount filesystem and genext2fs is missing"
  116. fi
  117. else
  118. echo "done"
  119. fi
  120. cleanup() {
  121. if [ -d mnt ]; then
  122. if [ $use_genext2fs = 0 ] ; then
  123. printf "unmounting filesystem... "
  124. umount mnt || ( sleep 1 && sync && umount mnt )
  125. rmdir mnt
  126. else
  127. rm -rf mnt
  128. fi
  129. if [ "$(uname -s)" = "OpenBSD" ]; then
  130. vnconfig -u "$VND"
  131. elif [ "$(uname -s)" = "FreeBSD" ]; then
  132. mdconfig -d -u "$MD"
  133. fi
  134. echo "done"
  135. fi
  136. }
  137. trap cleanup EXIT
  138. script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
  139. "$script_path/build-root-filesystem.sh"
  140. if [ $use_genext2fs = 1 ]; then
  141. # regenerate new image, since genext2fs is unable to reuse the previously written image.
  142. # genext2fs is very slow in generating big images, so I use a smaller image here. size can be updated
  143. # if it's not enough.
  144. # not using "-I $INODE_SIZE" since it hangs. Serenity handles whatever default this uses instead.
  145. genext2fs -B 4096 -b $((DISK_SIZE_BYTES / 4096)) -d mnt _disk_image || die "try increasing image size (genext2fs -b)"
  146. # if using docker with shared mount, file is created as root, so make it writable for users
  147. chmod 0666 _disk_image
  148. fi