Comment 3 for bug 2056570

Revision history for this message
Olivier Gayot (ogayot) wrote (last edit ):

Without the --mount-proc option, calling `systemctl daemon-reload` in the chroot prints out "Running in chroot, ignoring command 'daemon-reload'" and then exits with status 0.

With the --mount-proc option, calling `systemctl daemon-reload` in the chroot fails with "Failed to connect to bus: No data available" and fails with status 100.

To determine if we are running in a chroot, systemd calls fstatat(2) on / and then fstatat(2) on /proc/1/root. It then compares the resulting structures, looking specially at the inode number, inode type and backing device. If anything looks different, systemd assumes we are in a chroot.

Using stat(1), we can observe what happens:

Without the --mount-proc option, the backing device (i.e. "Device") is different, therefore systemd assumes we are in a chroot:

# stat -L / /proc/1/root
          File: /
          Size: 4096 Blocks: 8 IO Block: 4096 directory
=> Device: 252,0 Inode: 2 Links: 20
        Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
        Access: 2024-03-11 08:01:50.538756312 +0000
        Modify: 2024-03-11 08:01:49.398777854 +0000
        Change: 2024-03-11 08:01:49.398777854 +0000
         Birth: 2024-03-11 08:00:36.000000000 +0000
          File: /proc/1/root
          Size: 260 Blocks: 0 IO Block: 4096 directory
=> Device: 0,28 Inode: 2 Links: 1
        Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
        Access: 2024-03-11 08:06:22.017527026 +0000
        Modify: 2024-03-11 08:00:26.458886048 +0000
        Change: 2024-03-11 08:00:26.458886048 +0000
         Birth: 2024-03-11 07:58:30.876000000 +0000

But with the --mount-proc option, the structures look identical, therefore systemd thinks we are not running in a chroot:

          File: /
          Size: 4096 Blocks: 8 IO Block: 4096 directory
=> Device: 252,0 Inode: 2 Links: 20
        Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
        Access: 2024-03-11 08:01:50.538756312 +0000
        Modify: 2024-03-11 08:01:49.398777854 +0000
        Change: 2024-03-11 08:01:49.398777854 +0000
         Birth: 2024-03-11 08:00:36.000000000 +0000
          File: /proc/1/root
          Size: 4096 Blocks: 8 IO Block: 4096 directory
=> Device: 252,0 Inode: 2 Links: 20
        Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
        Access: 2024-03-11 08:01:50.538756312 +0000
        Modify: 2024-03-11 08:01:49.398777854 +0000
        Change: 2024-03-11 08:01:49.398777854 +0000
         Birth: 2024-03-11 08:00:36.000000000 +0000

Explanation
-----------
* When we run a command in a ChrootableTarget, we have:
  ** /proc bind mounted to /target/proc
  ** /sys bind mounted to /target/sys
  ** /run bind mounted to /target/run
  ** /dev bind mounted to /target/dev

* When we run, `unshare --pid --fork chroot /target apt-get ...`
  ** the content of /target/proc is inherited from outside the chroot, because of the bind-mount.
  ** /target/proc/1 corresponds to the process with PID 1 in the "parent" PID namespace (which is the systemd/init process)
  ** /target/proc/1/root is therefore the "root" of the systemd process, which is outside of the chroot
  ** in other words /target/proc/1/root == /
  ** systemd effectively compares /target/proc/1/root with /target and since they are different, it assumes we are in a chroot.

* When we run, `unshare --pid --fork --mount-proc=/target chroot /target apt-get ...`
  ** the content of /target/proc is fresh (the bind-mount is masked)
  ** /target/proc/1 corresponds to the process with PID 1 in the "child" PID namespace
  ** /target/proc/1/root is therefore the "root" of the chroot
  ** in other words /target/proc/1/root == /target
  ** systemd effectively compares /target/proc/1/root with /target and since they are identical, it assumes we are /not/ in a chroot.