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