Nerveware


Root file systems

Generating root file systems (rootfs) isn't the hardest thing to do in embedded Linux. Debian, for instance, already has a lot of scripts that help one to generate them. You only need to know a few details about mounting and changing your root to get things up and running fast. In this tutorial I will explain how to generate a Debian rootfs, add packages from other repositories, configure the rootfs as normal user and archiving it afterwards.

Rootfs are typically created in four steps: download packages, add custom packages, configure rootfs, archive root file system.

Before we begin

If you plan to generate a rootfs for another architecture, you also need to have a cross compiler. (For instance: gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf). Furthermore, to create rootfss, you need to install the following packagages.

$ apt-get install fakeroot qemu qemu-user-static proot fakeroot multistrap make gcc $ export PATH="${PATH}:/usr/sbin:/sbin"

Download packages

Multistrap is a tool that we will use to generate the initial rootfs. It uses a configuration file. To make sure that every package is downloaded and installed correctly, we run multistrap in a fakeroot environment. Fakeroot fakes root privileges for file manupilation.

The configuration file might look as follows. Note that the packages must remain on a single line. Below, I'm creating a Debian Buster filesystem for the armhf architecture. This file is called multistrap.conf

[General] arch=armhf directory=rootfs cleanup=true noauth=true unpack=true aptsources=Buster debootstrap=Buster addimportant=true [Buster] packages=systemd util-linux bash coreutils psmisc locales dbus procps policykit-1 sed grep apt-utils apt debconf debianutils dpkg dpkg-sig rcconf base-files base-passwd udev libudev-dev kmod ca-certificates ssh openssl libcurl4-openssl-dev net-tools mount vim libmodbus5 file source=http://ftp.nl.debian.org/debian keyring=debian-archive-keyring suite=buster

Execute the following command to generate the root file system

$ fakeroot -- /usr/sbin/multistrap -f multistrap.conf

Multistrap insecure repository error

Note: In more recent multistrap might complain about insecure repositories (2.2.10 at the time of writing). The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 648ACFD622F3D138 NO_PUBKEY 0E98404D386FA1D9 NO_PUBKEY DCC9EFBF77E11517

Copy multistrap to cwd and apply the patch below.

--- /usr/sbin/multistrap 2018-11-21 15:00:46.000000000 +0100 +++ multistrap 2021-11-01 09:40:52.288606233 +0100 @@ -340,7 +340,7 @@ my $apt_mark = "APT_CONFIG=" . shellescape($tmp_apt_conf) . " apt-mark $config_str"; printf (_g("Getting package lists: %s update\n"), $apt_get); -$retval = system ("$apt_get update"); +$retval = system ("$apt_get update --allow-insecure-repositories"); $retval >>= 8; die (sprintf (_g("apt update failed. Exit value: %d\n"), $retval)) if ($retval != 0);

Of course, now the command to generate the root file system is as follows.

$ fakeroot -- ./multistrap -f multistrap.conf

Add custom packages

Usually you have prerequisites for a file system. In my own rootfs I enabled ssh login with password for root and set the timeout to 15 seconds in the systemd networking service. (Don't get me starting on systemd, though). There are also some things you have to add by default. These are:

FilenameDesciption
etc/hostnameAdd a hostname.
etc/fstabMount /dev/mmcblk0p1 on /, dev, proc and sys.
etc/mtabln -s ../proc/self/mounts ./etc/mtab
etc/groupAdd root to inet and net_raw groups to make ping work. (inet:x:3003:root, net_raw:x:3004:root). These should be added:
  • root:x:0:
  • mail:*:8:
  • utmp:*:43:
etc/timezoneEurope/Amsterdam. Make sure that timezone isn't asked when we automate this in a makefile.

I prefer to store the prerequisites in a directory named 'skeleton'. Now this directory is created, they must be copied to the rootfs that's being generated.

$ cp -aRf skeleton/* rootfs/.

Workarounds

Within Debian there are some issues between proot and PAM authentication if proot is run by an unprivileged user. This affects the following programs:

This issue prevents configuring systemd and setting passwords or user information. This can be solved easly by overwriting the function in which the error occurs.

$ cat fake_audit_log_acct_message.c int audit_log_acct_message(int audit_fd, int type, const char *pgname, const char *op, const char *name, unsigned int id, const char *host, const char *addr, const char *tty, int result) { return 0; }

Make sure you cross compile it. You'll have to prepend `dpkg` and `chpasswd` commands with:

LD_LIBRARY_PATH=/usr/lib/ LD_PRELOAD=libfakeaudit.so.0.0

I stored this in workarounds/pam/.

References:

Configure the root file system

To ensure that a rootfs is working correctly, several files need to be linked from the host to the rootfs. These files are:

Use proot to configure the rootfs, since it doesn't need root privileges as chroot needs. Before the shell in proot is usable, export the PATH, DEBIAN_DRONTEND and DEBCONFG_NONINTERACTIVE_SEEN environment variables. This makes sure that every command is found and no questions are asked that are already defined.

$ cp `which qemu-arm-static` rootfs/usr/bin/. $ proot -0 -w / -r rootfs -b /dev -b /proc -b /sys -b /etc/resolv.conf -b workarounds qemu-arm-static /bin/bash $ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; $ export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true;

Configure the rootfs with the following commands.

$ /var/lib/dpkg/info/dash.preinst install; $ LD_LIBRARY_PATH=/usr/lib/ LD_PRELOAD=libfakeaudit.so.0.0 dpkg --configure -a; $ echo "root:root" | LD_LIBRARY_PATH=/usr/lib/ LD_PRELOAD=libfakeaudit.so.0.0 chpasswd;

Reconfigure packages

It might be the case that packages remain unconfigured. If this happens, the message below will be printed. Base-files and bash usally complain about missing groups.

Errors were encountered while processing: base-files bash dbus libpam-systemd:armhf policykit-1

Through the use of dpkg-reconfigure, these packages can be configured.

$ dpkg-reconfigure base-files bash dbus libpam-systemd:armhf policykit-1

Archive root file system

Archiving is the last part. Make sure that root owns every file. If you've added an additional user with a home directory, you should exclude that directory with `--exclude="./home/user"` and add it afterwards.

$ tar -czpf rootfs.tar -C rootfs --owner=0 --group=0 .

Example excluding /home/user.

$ tar -czpf rootfs.tar -C rootfs --owner=0 --group=0 --exclude="./home/user" . $ tar -rpf rootfs.tar -C rootfs --owner=1000 --group=1000 ./home/user