How can I build reform-system-image on a non-Debian system?

I would like to build a system image, but the build script seems to be specific to Debian-based distributions. If I try to run the mkimage script, for example, I get

+ set -e
+ DEPS='mmdebstrap genext2fs e2fsprogs binfmt-support git mount arch-test qemu-user-static parted'
++ dpkg-query --showformat '${db:Status-Status}\n' --show mmdebstrap genext2fs e2fsprogs binfmt-support git mount arch-test qemu-user-static parted
++ sort -u
dpkg-query: no packages found matching mmdebstrap
dpkg-query: no packages found matching genext2fs
dpkg-query: no packages found matching e2fsprogs
dpkg-query: no packages found matching binfmt-support
dpkg-query: no packages found matching git
dpkg-query: no packages found matching mount
dpkg-query: no packages found matching arch-test
dpkg-query: no packages found matching qemu-user-static
dpkg-query: no packages found matching parted
+ '[' '' '!=' installed ']'
+ echo 'Not all dependencies of this script are installed.'
Not all dependencies of this script are installed.
+ echo 'Run the following command to install them:'
Run the following command to install them:
+ echo

+ echo '    sudo apt install mmdebstrap genext2fs e2fsprogs binfmt-support git mount arch-test qemu-user-static parted'
    sudo apt install mmdebstrap genext2fs e2fsprogs binfmt-support git mount arch-test qemu-user-static parted
+ exit 1

even with mmdebstrap on my path (cloned from its git repo), since none of my RPM packages are found with dpkg-query. I know one way to do this would be to set up a Debian or Ubuntu VM, but that seems overkill for this problem. A container may be the way to do it, but I don’t have much experience with them.

Is there a straightforward way to do this in Fedora?

Maybe. The script is completely untested on platforms other than Debian, so you may run into interesting problems that you need to solve with your Fedora-specific knowledge. I cannot help you with Fedora.

We are using mmdebstrap to create bit-by-bit identical results and to be able to run the whole thing without needing superuser privileges. I’m the mmdebstrap maintainer and I haven’t heard from anyone using it successfully on Fedora. I think most problems might come from trying to run mmdebstrap on a non-Debian system. If you run into problems, feel free to report them on the mmdebstrap bug tracker: Issues - mmdebstrap - Muffin Gitea

This is not exactly what you asked for but it might be a workaround: you could install a Debian system in a “change root” (chroot) environment and setup the build environment in there. If you have never done that, it is not as intimidating as it sounds: it is very standard procedure for cross-architecture builds and well-documented. It should work on Fedora too I believe. Search for “debian chroot bootstrap” for howtos.

This approach has little overhead compared to a VM.

That’s a good idea, I pretty much only use chroot for cross-architecture work, but hadn’t considered it for this case. I’ve made some progress with the container route, but if that doesn’t work I’ll try a chroot before falling back to a full-on VM.

Update: the build fails with tar: setxattrat: Cannot set 'security.selinux' extended attribute for file './etc': Operation not supported repeated for ./etc and every other file and directory in the image. I found a suggestion online to disable selinux on the host system, so I did, but that didn’t change anything.

The chroot build fails with

Reading package lists...
I: creating tarball...
I: done
I: removing tempdir /reform-system-image/reform2-imx8mq/mmdebstrap.AtndeOuHMq...
I: success in 2145.3719 seconds
+ mmtarfilter '--path-exclude=*' --path-include=/boot '--path-include=/boot/*' --strip-components=2
+ genext2fs --block-size 1024 --size-in-blocks 499712 --bytes-per-inode 16384 --tarball - boot.img
copying from tar archive -
./mkimage.sh: line 59: 485992 Done                    mmtarfilter --path-exclude='*' --path-include=/boot --path-include='/boot/*' --strip-components=2 < target-userland.tar
     485993 Segmentation fault      (core dumped) | genext2fs --block-size 1024 --size-in-blocks $((BOOTSIZE*1024)) --bytes-per-inode 16384 --tarball - boot.img

both in the “main” and “sysimage-v3” branches.

In theory, you will not need a full VM or container to run the reform-system-image script. A chroot will provide less isolation but it should be enough for this use-case.

Can you show log output for the xattr problem? The log you show doesn’t include any mention of xattr.

What worries me in your log is that Segmentation fault. Are you familiar with how to debug these with gdb?

The only interesting git branch for you is “main”. The “sysimage-v3” branch is outdated and all of it has been merged into “main”.

I’ve been trying for the past couple of days to get a log out of the container, but it just won’t write anything. I’m currently running it with ./mkimage.sh > log.txt 2>&1 which AFAIK should redirect both stdout and stderr to a log file in the work directory, and the container doesn’t show any console output when I run it with podman, but when I mount the container and go to the work directory, there’s no log file.

My last post may have been worded confusingly - there are two different attempts there, one in a container and one in a chroot. The container is the one that gave me the xattr errors, the chroot is the one that gave me the segfault. The one-line error is from the container, the multi-line log is from the chroot.

I’m familiar with using gdb on binaries I’ve built with debugging symbols, but not regular binaries. Would I need to build genext2fs with debugging symbols in the chroot to debug it, or is there another way?

I’m the genext2fs maintainer in Debian. Can you create a reproducer script that shows me your segfault in a Debian chroot?

Unable to reproduce this now. Here’s the script I was putting together:

#!/bin/bash

# Create the Debian environment
debootstrap unstable debian-chroot http://deb.debian.org/debian/

# Probably unnecessary (I think debootstrap handles
# this) but ensure chroot has network access
cp /etc/resolv.conf debian-chroot/etc/

# Run chroot stuff
cat <<EOF | chroot debian-chroot /bin/bash

# Set up apt wait function
# Credit: https://gist.github.com/tedivm/e11ebfdc25dc1d7935a3d5640a1f1c90
apt_wait () {
  while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
    sleep 1
  done
  while sudo fuser /var/lib/apt/lists/lock >/dev/null 2>&1 ; do
    sleep 1
  done
  if [ -f /var/log/unattended-upgrades/unattended-upgrades.log ]; then
    while sudo fuser /var/log/unattended-upgrades/unattended-upgrades.log >/dev/null 2>&1 ; do
      sleep 1
    done
  fi
}

# Get dependencies and ensure locale is set repeatably
apt install -y mmdebstrap \
genext2fs \
e2fsprogs \
binfmt-support \
git \
mount \
arch-test \
qemu-user-static \
parted \
locales

# Ensure apt is done before moving on
apt_wait

# Ensure locales are generated repeatably, not sure if
# debootstrap uses a standard locale or your system locale
sed -i 's/# \(en_US\.UTF-8 .*\)/\1/' /etc/locale.gen && \
locale-gen
export LANG="en_US.UTF-8" && \
export LC_ALL="en_US.UTF-8"

# Get the code
git clone https://source.mnt.re/reform/reform-system-image.git

# cd and build
cd reform-system-image/reform2-imx8mq

./mkimage.sh

EOF

That successfully built the image. The script may have some extraneous parts in it; I added the apt_wait function since I got some locale warnings when installing packages, so I thought the script was kicking off apt and then immediately moving on to the next line, but even with apt_wait it gives me those warnings and then generates the locale at the end of the package installation.

You do not need this apt_wait function. If you do, then that’s a bug which should be reported. You also do not need to check for unattended-upgrades. This is a chroot without an init system, so cron can never kick this off. You didn’t even install the unattended-upgrades package. Where is that code from? And why are you generating locales? Running that script doesn’t need any locales to be set up.

Can you just output the error message you are getting so that I can see the problem that you are seeing?

Copied and pasted from A BASH function to wait for `apt` to finish and release all locks. · GitHub. When I made my first attempt at doing this, using a container, the locale was set to “C” for everything and the mkimage.sh script would fail due to certain characters (I think in a tar file that gets extracted at some point) not being supported. From what I found online, that was due to having the C locale, so I switched to en_US.UTF-8 and the error didn’t reoccur. When I run debootstrap, then run locale in the chroot, it shows en_US.UTF-8, but I’m not sure if this is a result of that being the host system locale or if it’s set by default. Since mkimage seems to be dependent on locale for success, I thought it would be a good idea to guarantee that the locale was one that would work.

From deleting the chroot and rerunning the script, here’s what I see when the dependency installation begins:

Get:87 http://deb.debian.org/debian unstable/main amd64 xauth amd64 1:1.1.1-1 [40.7 kB]
Fetched 104 MB in 4s (24.0 MB/s)
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
        LANGUAGE = "",
        LC_ALL = (unset),
        LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
Extracting templates from packages: 100%
Preconfiguring packages ...
E: Can not write log (Is /dev/pts mounted?) - posix_openpt (19: No such device)
Selecting previously unselected package libpipeline1:amd64.
(Reading database ... 11167 files and directories currently installed.)
Preparing to unpack .../libpipeline1_1.5.6-3_amd64.deb ...
Unpacking libpipeline1:amd64 (1.5.6-3) ...

and later,

Setting up ca-certificates (20211016) ...
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
Updating certificates in /etc/ssl/certs...
127 added, 0 removed; done.
Setting up libgprofng0:amd64 (2.38.90.20220713-2) ...

Okay, so what is happening here is the following:

You, as the user running this script have some locale configured on your system and which locale your user is using is stored in a set of environment variables like LANG. When you run something like this:

chroot ./debian-rootfs perl -e exit

Then the chroot call will not change your environment variables. So if your system on the outside is configured to use locale X but that locale is not available inside ./debian-rootfs then perl will complain about that.

There are two ways around that:

  • do not pass your outer system’s locale on into the chroot through your environment variables, or
  • make the locales you are using outside your chroot available inside the chroot

Perl complaining is usually harmless but if it annoys you too much, then just do either of the two.

But while what perl prints is just a warning, the locale actually becomes important later when you prepare the tarball. For that operation your C locale will not be enough because some filenames need more characters than ASCII. One way around that is to set your locale explicitly to one that uses UTF-8 like en_US.UTF-8 but a locale does two things:

  • configure units, currencies, number formats, sorting order
  • set the character encoding

For the script you are only interested in the latter. Setting the former to what is used in the united states is not useful. The script has nothing to do with how things are handled in the USA. So instead of using en_US.UTF-8, you can use C.UTF-8 instead. This means that you get UTF-8 encoding without region-specific settings. It also has the advantage that with C.UTF-8 you do not need to install and configure the locales package. It is available by default. Demo time:

$ sudo chroot debian-unstable perl -e 'printf "foobar\n"'
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = "en_US:en",
	LC_ALL = (unset),
	LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
foobar

And now with LANG=C.UTF-8:

$ sudo chroot debian-unstable env LANG=C.UTF-8 perl -e 'printf "foobar\n"'
foobar

And please don’t use A BASH function to wait for `apt` to finish and release all locks. · GitHub – that script is not even useful in cases where you run apt in a background process (which you are not doing) because it introduces a race-condition between checking for the lockfile and actually acquiring the lock afterwards.

Good to know, I haven’t dealt with locales beyond following instructions to generate one when setting up an image for some distros, so I wasn’t aware that C.UTF-8 was available without the locales package. Avoiding that installation + file editing + new locale generation is definitely better.