1#!/bin/bash
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18set -e
19set -u
20
21SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
22
23usage() {
24  echo -n "usage: $0 [-h] [-s bullseye|bullseye-cuttlefish|bullseye-rockpi|bullseye-server] "
25  echo -n "[-a i386|amd64|armhf|arm64] -k /path/to/kernel "
26  echo -n "-i /path/to/initramfs.gz [-d /path/to/dtb:subdir] "
27  echo "[-m http://mirror/debian] [-n rootfs|disk] [-r initrd] [-e] [-g]"
28  exit 1
29}
30
31mirror=http://ftp.debian.org/debian
32embed_kernel_initrd_dtb=0
33install_grub=0
34suite=bullseye
35arch=amd64
36
37dtb_subdir=
38initramfs=
39kernel=
40ramdisk=
41disk=
42dtb=
43
44while getopts ":hs:a:m:n:r:k:O:i:d:eg" opt; do
45  case "${opt}" in
46    h)
47      usage
48      ;;
49    s)
50      if [[ "${OPTARG%-*}" != "bullseye" ]]; then
51        echo "Invalid suite: ${OPTARG}" >&2
52        usage
53      fi
54      suite="${OPTARG}"
55      ;;
56    a)
57      arch="${OPTARG}"
58      ;;
59    m)
60      mirror="${OPTARG}"
61      ;;
62    n)
63      disk="${OPTARG}"
64      ;;
65    r)
66      ramdisk="${OPTARG}"
67      ;;
68    k)
69      kernel="${OPTARG}"
70      ;;
71    O)
72      extradeb="${OPTARG}"
73      ;;
74    i)
75      initramfs="${OPTARG}"
76      ;;
77    d)
78      dtb="${OPTARG%:*}"
79      if [ "${OPTARG#*:}" != "${dtb}" ]; then
80        dtb_subdir="${OPTARG#*:}/"
81      fi
82      ;;
83    e)
84      embed_kernel_initrd_dtb=1
85      ;;
86    g)
87      install_grub=1
88      ;;
89    \?)
90      echo "Invalid option: ${OPTARG}" >&2
91      usage
92      ;;
93    :)
94      echo "Invalid option: ${OPTARG} requires an argument" >&2
95      usage
96      ;;
97  esac
98done
99
100# Disable Debian's "persistent" network device renaming
101cmdline="net.ifnames=0 rw 8250.nr_uarts=2 PATH=/usr/sbin:/bin:/usr/bin"
102cmdline="${cmdline} embed_kernel_initrd_dtb=${embed_kernel_initrd_dtb}"
103cmdline="${cmdline} install_grub=${install_grub}"
104
105case "${arch}" in
106  i386)
107    cmdline="${cmdline} console=ttyS0 exitcode=/dev/ttyS1"
108    machine="pc-i440fx-2.8,accel=kvm"
109    qemu="qemu-system-i386"
110    partguid="8303"
111    cpu="max"
112    ;;
113  amd64)
114    cmdline="${cmdline} console=ttyS0 exitcode=/dev/ttyS1"
115    machine="pc-i440fx-2.8,accel=kvm"
116    qemu="qemu-system-x86_64"
117    partguid="8304"
118    cpu="max"
119    ;;
120  armhf)
121    cmdline="${cmdline} console=ttyAMA0 exitcode=/dev/ttyS0"
122    machine="virt,gic-version=2"
123    qemu="qemu-system-arm"
124    partguid="8307"
125    cpu="cortex-a15"
126    ;;
127  arm64)
128    cmdline="${cmdline} console=ttyAMA0 exitcode=/dev/ttyS0"
129    machine="virt,gic-version=2"
130    qemu="qemu-system-aarch64"
131    partguid="8305"
132    cpu="cortex-a53" # "max" is too slow
133    ;;
134  *)
135    echo "Invalid arch: ${OPTARG}" >&2
136    usage
137    ;;
138esac
139
140if [[ -z "${disk}" ]]; then
141  if [[ "${install_grub}" = "1" ]]; then
142    base_image_name=disk
143  else
144    base_image_name=rootfs
145  fi
146  disk="${base_image_name}.${arch}.${suite}.$(date +%Y%m%d)"
147fi
148disk=$(realpath "${disk}")
149
150if [[ -z "${ramdisk}" ]]; then
151  ramdisk="initrd.${arch}.${suite}.$(date +%Y%m%d)"
152fi
153ramdisk=$(realpath "${ramdisk}")
154
155if [[ -z "${kernel}" ]]; then
156  echo "$0: Path to kernel image must be specified (with '-k')"
157  usage
158elif [[ ! -e "${kernel}" ]]; then
159  echo "$0: Kernel image not found at '${kernel}'"
160  exit 2
161fi
162
163if [[ -z "${initramfs}" ]]; then
164  echo "Path to initial ramdisk image must be specified (with '-i')"
165  usage
166elif [[ ! -e "${initramfs}" ]]; then
167  echo "Initial ramdisk image not found at '${initramfs}'"
168  exit 3
169fi
170
171# Sometimes it isn't obvious when the script fails
172failure() {
173  echo "Filesystem generation process failed." >&2
174  rm -f "${disk}" "${ramdisk}"
175}
176trap failure ERR
177
178# Import the package list for this release
179packages=$(cpp "${SCRIPT_DIR}/rootfs/${suite}.list" | grep -v "^#" | xargs | tr -s ' ' ',')
180
181# For the debootstrap intermediates
182tmpdir=$(mktemp -d)
183tmpdir_remove() {
184  echo "Removing temporary files.." >&2
185  sudo rm -rf "${tmpdir}"
186}
187trap tmpdir_remove EXIT
188
189workdir="${tmpdir}/_"
190mkdir "${workdir}"
191chmod 0755 "${workdir}"
192sudo chown root:root "${workdir}"
193
194# Run the debootstrap first
195cd "${workdir}"
196
197retries=5
198while ! sudo debootstrap --arch="${arch}" --variant=minbase --include="${packages}" \
199        --foreign "${suite%-*}" . "${mirror}"; do
200    retries=$((${retries} - 1))
201    if [ ${retries} -le 0 ]; then
202	failure
203	exit 1
204    fi
205    echo "debootstrap failed - trying again - ${retries} retries left"
206done
207
208# Copy some bootstrapping scripts into the rootfs
209sudo cp -a "${SCRIPT_DIR}"/rootfs/*.sh root/
210sudo cp -a "${SCRIPT_DIR}"/rootfs/net_test.sh sbin/net_test.sh
211sudo chown root:root sbin/net_test.sh
212
213# Extract the ramdisk to bootstrap with to /
214lz4 -lcd "${initramfs}" | sudo cpio -idum lib/modules/*
215
216# Create /host, for the pivot_root and 9p mount use cases
217sudo mkdir host
218
219# debootstrap workaround: Run debootstrap in docker sometimes causes the
220# /proc being a symlink in first stage. We need to fix the symlink to an empty
221# directory.
222if [ -L "${workdir}/proc" ]; then
223  echo "/proc in debootstrap 1st stage is a symlink. Fixed!"
224  sudo rm -f "${workdir}/proc"
225  sudo mkdir "${workdir}/proc"
226fi
227
228# Leave the workdir, to build the filesystem
229cd -
230
231# For the initial ramdisk, and later for the final rootfs
232mount=$(mktemp -d)
233mount_remove() {
234  rmdir "${mount}"
235  tmpdir_remove
236}
237trap mount_remove EXIT
238
239# The initial ramdisk filesystem must be <=512M, or QEMU's -initrd
240# option won't touch it
241initrd=$(mktemp)
242initrd_remove() {
243  rm -f "${initrd}"
244  mount_remove
245}
246trap initrd_remove EXIT
247truncate -s 512M "${initrd}"
248/sbin/mke2fs -F -t ext4 -L ROOT "${initrd}"
249
250# Mount the new filesystem locally
251sudo mount -o loop -t ext4 "${initrd}" "${mount}"
252image_unmount() {
253  sudo umount "${mount}"
254  initrd_remove
255}
256trap image_unmount EXIT
257
258# Copy the patched debootstrap results into the new filesystem
259sudo cp -a "${workdir}"/* "${mount}"
260sudo rm -rf "${workdir}"
261
262# Unmount the initial ramdisk
263sudo umount "${mount}"
264trap initrd_remove EXIT
265
266if [[ "${install_grub}" = 1 ]]; then
267  part_num=0
268  # $1 partition size
269  # $2 gpt partition type
270  # $3 partition name
271  # $4 bypass alignment checks (use on <1MB partitions only)
272  # $5 partition attribute bit to set
273  sgdisk() {
274    part_num=$((part_num+1))
275    [[ -n "${4:-}" ]] && prefix="-a1" || prefix=
276    [[ -n "${5:-}" ]] && suffix="-A:${part_num}:set:$5" || suffix=
277    /sbin/sgdisk ${prefix} \
278      "-n:${part_num}:$1" "-t:${part_num}:$2" "-c:${part_num}:$3" \
279      ${suffix} "${disk}" >/dev/null 2>&1
280  }
281  # If there's a bootloader, we need to make space for the GPT header, GPT
282  # footer and EFI system partition (legacy boot is not supported)
283  # Keep this simple - modern gdisk reserves 1MB for the GPT header and
284  # assumes all partitions are 1MB aligned
285  truncate -s "$((1 + 128 + 10 * 1024 + 1))M" "${disk}"
286  /sbin/sgdisk --zap-all "${disk}" >/dev/null 2>&1
287  # On RockPi devices, steal a bit of space at the start of the disk for
288  # some special bootloader partitions. Some of these have to start/end
289  # at specific offsets as well
290  if [[ "${suite#*-}" = "rockpi" ]]; then
291    # See https://opensource.rock-chips.com/wiki_Boot_option
292    # Keep in sync with rootfs/*-rockpi.sh
293    sgdisk "64:8127"   "8301"        "idbloader" "true"
294    sgdisk "8128:+64"  "8301"        "uboot_env" "true"
295    sgdisk "8M:+4M"    "8301"        "uboot"
296    sgdisk "12M:+4M"   "8301"        "trust"
297    sgdisk "16M:+1M"   "8301"        "misc"
298    sgdisk "17M:+128M" "ef00"        "esp"       ""     "0"
299    sgdisk "145M:0"    "8305"        "rootfs"    ""     "2"
300    system_partition="6"
301    rootfs_partition="7"
302  else
303    sgdisk "0:+128M"   "ef00"        "esp"       ""     "0"
304    sgdisk "0:0"       "${partguid}" "rootfs"    ""     "2"
305    system_partition="1"
306    rootfs_partition="2"
307  fi
308
309  # Create an empty EFI system partition; it will be initialized later
310  system_partition_start=$(partx -g -o START -s -n "${system_partition}" "${disk}" | xargs)
311  system_partition_end=$(partx -g -o END -s -n "${system_partition}" "${disk}" | xargs)
312  system_partition_num_sectors=$((${system_partition_end} - ${system_partition_start} + 1))
313  system_partition_num_vfat_blocks=$((${system_partition_num_sectors} / 2))
314  /sbin/mkfs.vfat -n SYSTEM -F 16 --offset=${system_partition_start} "${disk}" ${system_partition_num_vfat_blocks} >/dev/null
315  # Copy the rootfs to just after the EFI system partition
316  rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
317  rootfs_partition_end=$(partx -g -o END -s -n "${rootfs_partition}" "${disk}" | xargs)
318  rootfs_partition_num_sectors=$((${rootfs_partition_end} - ${rootfs_partition_start} + 1))
319  rootfs_partition_offset=$((${rootfs_partition_start} * 512))
320  rootfs_partition_size=$((${rootfs_partition_num_sectors} * 512))
321  dd if="${initrd}" of="${disk}" bs=512 seek="${rootfs_partition_start}" conv=fsync,notrunc 2>/dev/null
322  /sbin/e2fsck -p -f "${disk}"?offset=${rootfs_partition_offset} || true
323  disksize=$(stat -c %s "${disk}")
324  /sbin/resize2fs "${disk}"?offset=${rootfs_partition_offset} ${rootfs_partition_num_sectors}s
325  truncate -s "${disksize}" "${disk}"
326  /sbin/sgdisk -e "${disk}"
327  /sbin/e2fsck -p -f "${disk}"?offset=${rootfs_partition_offset} || true
328  /sbin/e2fsck -fy "${disk}"?offset=${rootfs_partition_offset} || true
329else
330  # If there's no bootloader, the initrd is the disk image
331  cp -a "${initrd}" "${disk}"
332  truncate -s 10G "${disk}"
333  /sbin/e2fsck -p -f "${disk}" || true
334  /sbin/resize2fs "${disk}"
335  system_partition=
336  rootfs_partition="raw"
337fi
338
339# Create another fake block device for initrd.img writeout
340raw_initrd=$(mktemp)
341raw_initrd_remove() {
342  rm -f "${raw_initrd}"
343  initrd_remove
344}
345trap raw_initrd_remove EXIT
346truncate -s 64M "${raw_initrd}"
347
348# Get number of cores for qemu. Restrict the maximum value to 8.
349qemucpucores=$(nproc)
350if [[ ${qemucpucores} -gt 8 ]]; then
351  qemucpucores=8
352fi
353
354# Complete the bootstrap process using QEMU and the specified kernel
355${qemu} -machine "${machine}" -cpu "${cpu}" -m 2048 >&2 \
356  -kernel "${kernel}" -initrd "${initrd}" -no-user-config -nodefaults \
357  -no-reboot -display none -nographic -serial stdio -parallel none \
358  -smp "${qemucpucores}",sockets="${qemucpucores}",cores=1,threads=1 \
359  -object rng-random,id=objrng0,filename=/dev/urandom \
360  -device virtio-rng-pci-non-transitional,rng=objrng0,id=rng0,max-bytes=1024,period=2000 \
361  -drive file="${disk}",format=raw,if=none,aio=threads,id=drive-virtio-disk0 \
362  -device virtio-blk-pci-non-transitional,scsi=off,drive=drive-virtio-disk0 \
363  -drive file="${raw_initrd}",format=raw,if=none,aio=threads,id=drive-virtio-disk1 \
364  -device virtio-blk-pci-non-transitional,scsi=off,drive=drive-virtio-disk1 \
365  -chardev file,id=exitcode,path=exitcode \
366  -device pci-serial,chardev=exitcode \
367  -append "root=/dev/ram0 ramdisk_size=524288 init=/root/stage1.sh ${cmdline}"
368[[ -s exitcode ]] && exitcode=$(cat exitcode | tr -d '\r') || exitcode=2
369rm -f exitcode
370if [ "${exitcode}" != "0" ]; then
371  echo "Second stage debootstrap failed (err=${exitcode})"
372  exit "${exitcode}"
373fi
374
375# Fix up any issues from the unclean shutdown
376if [[ ${rootfs_partition} = "raw" ]]; then
377    sudo e2fsck -p -f "${disk}" || true
378else
379    rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
380    rootfs_partition_end=$(partx -g -o END -s -n "${rootfs_partition}" "${disk}" | xargs)
381    rootfs_partition_num_sectors=$((${rootfs_partition_end} - ${rootfs_partition_start} + 1))
382    rootfs_partition_offset=$((${rootfs_partition_start} * 512))
383    rootfs_partition_tempfile2=$(mktemp)
384    dd if="${disk}" of="${rootfs_partition_tempfile2}" bs=512 skip=${rootfs_partition_start} count=${rootfs_partition_num_sectors}
385    /sbin/e2fsck -p -f "${rootfs_partition_tempfile2}" || true
386    dd if="${rootfs_partition_tempfile2}" of="${disk}" bs=512 seek=${rootfs_partition_start} count=${rootfs_partition_num_sectors} conv=fsync,notrunc
387    rm -f "${rootfs_partition_tempfile2}"
388    /sbin/e2fsck -fy "${disk}"?offset=${rootfs_partition_offset} || true
389fi
390if [[ -n "${system_partition}" ]]; then
391  system_partition_start=$(partx -g -o START -s -n "${system_partition}" "${disk}" | xargs)
392  system_partition_end=$(partx -g -o END -s -n "${system_partition}" "${disk}" | xargs)
393  system_partition_num_sectors=$((${system_partition_end} - ${system_partition_start} + 1))
394  system_partition_offset=$((${system_partition_start} * 512))
395  system_partition_size=$((${system_partition_num_sectors} * 512))
396  system_partition_tempfile=$(mktemp)
397  dd if="${disk}" of="${system_partition_tempfile}" bs=512 skip=${system_partition_start} count=${system_partition_num_sectors}
398  /sbin/fsck.vfat -a "${system_partition_tempfile}" || true
399  dd if="${system_partition_tempfile}" of="${disk}" bs=512 seek=${system_partition_start} count=${system_partition_num_sectors} conv=fsync,notrunc
400  rm -f "${system_partition_tempfile}"
401fi
402
403# New workdir for the initrd extraction
404workdir="${tmpdir}/initrd"
405mkdir "${workdir}"
406chmod 0755 "${workdir}"
407sudo chown root:root "${workdir}"
408
409# Change into workdir to repack initramfs
410cd "${workdir}"
411
412# Process the initrd to remove kernel-specific metadata
413kernel_version=$(basename $(lz4 -lcd "${raw_initrd}" | sudo cpio -idumv 2>&1 | grep usr/lib/modules/ - | head -n1))
414lz4 -lcd "${raw_initrd}" | sudo cpio -idumv
415sudo rm -rf usr/lib/modules
416sudo mkdir -p usr/lib/modules
417
418# Debian symlinks /usr/lib to /lib, but we'd prefer the other way around
419# so that it more closely matches what happens in Android initramfs images.
420# This enables 'cat ramdiskA.img ramdiskB.img >ramdiskC.img' to "just work".
421sudo rm -f lib
422sudo mv usr/lib lib
423sudo ln -s /lib usr/lib
424
425# Repack the ramdisk to the final output
426find * | sudo cpio -H newc -o --quiet | lz4 -lc9 >"${ramdisk}"
427
428# Pack another ramdisk with the combined artifacts, for boot testing
429cat "${ramdisk}" "${initramfs}" >"${initrd}"
430
431# Leave workdir to boot-test combined initrd
432cd -
433
434rootfs_partition_tempfile=$(mktemp)
435# Mount the new filesystem locally
436if [[ ${rootfs_partition} = "raw" ]]; then
437    sudo mount -o loop -t ext4 "${disk}" "${mount}"
438else
439    rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
440    rootfs_partition_offset=$((${rootfs_partition_start} * 512))
441    rootfs_partition_end=$(partx -g -o END -s -n "${rootfs_partition}" "${disk}" | xargs)
442    rootfs_partition_num_sectors=$((${rootfs_partition_end} - ${rootfs_partition_start} + 1))
443    dd if="${disk}" of="${rootfs_partition_tempfile}" bs=512 skip=${rootfs_partition_start} count=${rootfs_partition_num_sectors}
444fi
445image_unmount2() {
446  sudo umount "${mount}"
447  raw_initrd_remove
448}
449if [[ ${rootfs_partition} = "raw" ]]; then
450    trap image_unmount2 EXIT
451fi
452
453# Embed the kernel and dtb images now, if requested
454if [[ ${rootfs_partition} = "raw" ]]; then
455    if [[ "${embed_kernel_initrd_dtb}" = "1" ]]; then
456	if [ -n "${dtb}" ]; then
457	    sudo mkdir -p "${mount}/boot/dtb/${dtb_subdir}"
458	    sudo cp -a "${dtb}" "${mount}/boot/dtb/${dtb_subdir}"
459	    sudo chown -R root:root "${mount}/boot/dtb/${dtb_subdir}"
460	fi
461	sudo cp -a "${kernel}" "${mount}/boot/vmlinuz-${kernel_version}"
462	sudo chown root:root "${mount}/boot/vmlinuz-${kernel_version}"
463    fi
464    sudo cp -a "${SCRIPT_DIR}"/rootfs/cron-run-installer-script "${mount}/etc/cron.d/cron-run-installer-script"
465    if [ -e "${extradeb}" ]; then
466	sudo cp -a "${extradeb}" "${mount}/root/extradeb.tar.gz"
467	sudo chown root:root "${mount}/root/extradeb.tar.gz"
468    fi
469else
470    if [[ "${embed_kernel_initrd_dtb}" = "1" ]]; then
471	if [ -n "${dtb}" ]; then
472	    e2mkdir -G 0 -O 0 "${rootfs_partition_tempfile}":"/boot/dtb/${dtb_subdir}"
473	    e2cp -G 0 -O 0 "${dtb}" "${rootfs_partition_tempfile}":"/boot/dtb/${dtb_subdir}"
474	fi
475	e2cp -G 0 -O 0 "${kernel}" "${rootfs_partition_tempfile}":"/boot/vmlinuz-${kernel_version}"
476    fi
477    e2cp -G 0 -O 0 "${SCRIPT_DIR}"/rootfs/cron-run-installer-script "${rootfs_partition_tempfile}":"/etc/cron.d/cron-run-installer-script"
478    if [ -e "${extradeb}" ]; then
479	e2cp -G 0 -O 0 "${extradeb}" "${rootfs_partition_tempfile}":"/root/extradeb.tar.gz"
480    fi
481fi
482
483# Unmount the initial ramdisk
484if [[ ${rootfs_partition} = "raw" ]]; then
485    sudo umount "${mount}"
486else
487    rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
488    rootfs_partition_end=$(partx -g -o END -s -n "${rootfs_partition}" "${disk}" | xargs)
489    rootfs_partition_num_sectors=$((${rootfs_partition_end} - ${rootfs_partition_start} + 1))
490    dd if="${rootfs_partition_tempfile}" of="${disk}" bs=512 seek=${rootfs_partition_start} count=${rootfs_partition_num_sectors} conv=fsync,notrunc
491fi
492rm -f "${rootfs_partition_tempfile}"
493trap raw_initrd_remove EXIT
494
495# Boot test the new system and run stage 3
496${qemu} -machine "${machine}" -cpu "${cpu}" -m 2048 >&2 \
497  -kernel "${kernel}" -initrd "${initrd}" -no-user-config -nodefaults \
498  -no-reboot -display none -nographic -serial stdio -parallel none \
499  -smp "${qemucpucores}",sockets="${qemucpucores}",cores=1,threads=1 \
500  -object rng-random,id=objrng0,filename=/dev/urandom \
501  -device virtio-rng-pci-non-transitional,rng=objrng0,id=rng0,max-bytes=1024,period=2000 \
502  -drive file="${disk}",format=raw,if=none,aio=threads,id=drive-virtio-disk0 \
503  -device virtio-blk-pci-non-transitional,scsi=off,drive=drive-virtio-disk0 \
504  -chardev file,id=exitcode,path=exitcode \
505  -device pci-serial,chardev=exitcode \
506  -netdev user,id=usernet0,ipv6=off \
507  -device virtio-net-pci-non-transitional,netdev=usernet0,id=net0 \
508  -append "root=LABEL=ROOT installer_script=/root/${suite}.sh ${cmdline}"
509[[ -s exitcode ]] && exitcode=$(cat exitcode | tr -d '\r') || exitcode=2
510rm -f exitcode
511if [ "${exitcode}" != "0" ]; then
512  echo "Root filesystem finalization failed (err=${exitcode})"
513  exit "${exitcode}"
514fi
515
516# Fix up any issues from the unclean shutdown
517if [[ ${rootfs_partition} = "raw" ]]; then
518    sudo e2fsck -p -f "${disk}" || true
519else
520    rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
521    rootfs_partition_end=$(partx -g -o END -s -n "${rootfs_partition}" "${disk}" | xargs)
522    rootfs_partition_num_sectors=$((${rootfs_partition_end} - ${rootfs_partition_start} + 1))
523    rootfs_partition_offset=$((${rootfs_partition_start} * 512))
524    rootfs_partition_tempfile2=$(mktemp)
525    dd if="${disk}" of="${rootfs_partition_tempfile2}" bs=512 skip=${rootfs_partition_start} count=${rootfs_partition_num_sectors}
526    /sbin/e2fsck -p -f "${rootfs_partition_tempfile2}" || true
527    dd if="${rootfs_partition_tempfile2}" of="${disk}" bs=512 seek=${rootfs_partition_start} count=${rootfs_partition_num_sectors} conv=fsync,notrunc
528    rm -f "${rootfs_partition_tempfile2}"
529    /sbin/e2fsck -fy "${disk}"?offset=${rootfs_partition_offset} || true
530fi
531if [[ -n "${system_partition}" ]]; then
532  system_partition_start=$(partx -g -o START -s -n "${system_partition}" "${disk}" | xargs)
533  system_partition_end=$(partx -g -o END -s -n "${system_partition}" "${disk}" | xargs)
534  system_partition_num_sectors=$((${system_partition_end} - ${system_partition_start} + 1))
535  system_partition_offset=$((${system_partition_start} * 512))
536  system_partition_size=$((${system_partition_num_sectors} * 512))
537  system_partition_tempfile=$(mktemp)
538  dd if="${disk}" of="${system_partition_tempfile}" bs=512 skip=${system_partition_start} count=${system_partition_num_sectors}
539  /sbin/fsck.vfat -a "${system_partition_tempfile}" || true
540  dd if="${system_partition_tempfile}" of="${disk}" bs=512 seek=${system_partition_start} count=${system_partition_num_sectors} conv=fsync,notrunc
541  rm -f "${system_partition_tempfile}"
542fi
543
544# Mount the final disk image locally
545if [[ ${rootfs_partition} = "raw" ]]; then
546    sudo mount -o loop -t ext4 "${disk}" "${mount}"
547else
548    rootfs_partition_start=$(partx -g -o START -s -n "${rootfs_partition}" "${disk}" | xargs)
549    rootfs_partition_offset=$((${rootfs_partition_start} * 512))
550    sudo mount -o loop,offset=${rootfs_partition_offset} -t ext4 "${disk}" "${mount}"
551fi
552image_unmount3() {
553  sudo umount "${mount}"
554  raw_initrd_remove
555}
556trap image_unmount3 EXIT
557
558# Fill the rest of the space with zeroes, to optimize compression
559sudo dd if=/dev/zero of="${mount}/sparse" bs=1M 2>/dev/null || true
560sudo rm -f "${mount}/sparse"
561
562echo "Debian ${suite} for ${arch} filesystem generated at '${disk}'."
563echo "Initial ramdisk generated at '${ramdisk}'."
564