Skip to content

fix(busybox): ensure /usr/bin/sh symlink is always created #2454 - #2455

Closed
challvy wants to merge 1 commit into
dracut-ng:mainfrom
challvy:main
Closed

fix(busybox): ensure /usr/bin/sh symlink is always created #2454#2455
challvy wants to merge 1 commit into
dracut-ng:mainfrom
challvy:main

Conversation

@challvy

@challvy challvy commented May 25, 2026

Copy link
Copy Markdown

POSIX mandates that /bin/sh must exist. On merged-usr systems this resolves to /usr/bin/sh. The busybox module creates applet symlinks at whatever path find_binary() returns, which depends on the DRACUT_PATH ordering (default: /usr/sbin /sbin /usr/bin /bin).

On Fedora 42+, the busybox package (1.37.0+) places applet symlinks under /usr/sbin/ as part of the /usr/sbin → /usr/bin unification transition. Since DRACUT_PATH searches /usr/sbin first, find_binary("sh") returns /usr/sbin/sh and the module only creates that symlink — leaving /usr/bin/sh (and thus /bin/sh) absent.

This breaks any script using #!/bin/sh as its shebang — notably squash-lib's init-squash.sh which serves as PID 1 in the squash loader. The kernel fails with "Failed to execute /init (error -2)".

Fix this by unconditionally ensuring /usr/bin/sh exists after the applet symlink loop. If find_binary already placed sh at /usr/bin/sh, the check is a no-op.

This pull request ensures /usr/bin/sh symlink is always created by the busybox module, fixing kdump squashfs boot failure on Fedora 42/43 where busybox places sh at /usr/sbin/sh due to DRACUT_PATH ordering.

Fixes: #2454

Checklist

  • I have tested it locally
  • I have reviewed and updated any documentation if relevant
  • I am providing new code and test(s) for it

POSIX mandates that /bin/sh must exist. On merged-usr systems this
resolves to /usr/bin/sh. The busybox module creates applet symlinks at
whatever path find_binary() returns, which depends on the DRACUT_PATH
ordering (default: /usr/sbin /sbin /usr/bin /bin).

On Fedora 42+, the busybox package (1.37.0+) places applet symlinks
under /usr/sbin/ as part of the /usr/sbin → /usr/bin unification
transition. Since DRACUT_PATH searches /usr/sbin first,
find_binary("sh") returns /usr/sbin/sh and the module only creates that
symlink — leaving /usr/bin/sh (and thus /bin/sh) absent.

This breaks any script using #!/bin/sh as its shebang — notably
squash-lib's init-squash.sh which serves as PID 1 in the squash loader.
The kernel fails with "Failed to execute /init (error -2)".

Fix this by unconditionally ensuring /usr/bin/sh exists after the applet
symlink loop. If find_binary already placed sh at /usr/bin/sh, the check
is a no-op.

Signed-off-by: challvy <challvy.tee@gmail.com>
@challvy
challvy requested a review from a team as a code owner May 25, 2026 11:53
@github-actions github-actions Bot added modules Issue tracker for all modules busybox Issues related to the busybox module labels May 25, 2026
@challvy

challvy commented May 25, 2026

Copy link
Copy Markdown
Author

I also created a bugreport in https://bugzilla.redhat.com/show_bug.cgi?id=2481179

@devkontrol

devkontrol commented May 25, 2026

Copy link
Copy Markdown
Collaborator

DRACUT_PATH has been removed - #1468 .

Fedora is slow to adapt latest dracut version, so Fedora contributors should make sure they consider upstream changes that are not yet in Fedora.

CC @pvalena @prudo1

@challvy

challvy commented May 25, 2026

Copy link
Copy Markdown
Author

DRACUT_PATH has been removed - #1468 .

@devkontrol I just tested this patch, but this change alone does not resolve the issue. The symptoms remain the same.

@devkontrol

Copy link
Copy Markdown
Collaborator

DRACUT_PATH has been removed - #1468 .

@devkontrol I just tested this patch, but this change alone does not resolve the issue. The symptoms remain the same.

Indeed. I did not mean to imply that this issue is fixed, but it makes this PR inconsistent with rest of dracut. As an example, the following line needs to change

(e.g. /usr/sbin/sh) depending on $DRACUT_PATH ordering, so create

# on merged-usr). find_binary may locate 'sh' at a non-standard path
# (e.g. /usr/sbin/sh) depending on $DRACUT_PATH ordering, so create
# the symlink explicitly if absent.
[[ -e "${_dstdir}/usr/bin/sh" ]] || ln_r /usr/bin/busybox /usr/bin/sh

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

[[ -L $initdir/bin/sh ]] || ln -sf busybox "${initdir}/bin/sh"

..to follow the established pattern for bash and dash module.

This would be less opinionated and easier to read and more consistent with rest of dracut ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@devkontrol Thanks for the suggestion. However, using $initdir directly doesn't work in the squash case:The busybox module is invoked twice during a squash build:

  • First during the main module install loop (no dstdir override) → installs into $initdir (becomes inner squashfs content)
  • Then again from squash_install() with dstdir=$squashdir → installs into $squashdir (becomes outer loader content)

If we use $initdir/bin/sh, the second invocation sees the symlink already created by the first invocation and skips, leaving the outer loader without /bin/sh — which is exactly the bug we're trying to fix.

The bash/dash modules don't hit this because squash-lib only depends on busybox; if they were also pulled into the loader, they'd have the same latent issue.Adopting your style preference but keeping _dstdir:

[[ -L "${_dstdir}/bin/sh" ]] || ln -sf busybox "${_dstdir}/bin/sh"

Does this look acceptable?

@bdrung

bdrung commented May 25, 2026

Copy link
Copy Markdown
Member

The directory structure looks like this on Fedora:

$ ls -l /usr/sbin /sbin /usr/bin /bin -d
lrwxrwxrwx 1 root root     7 Jan 16 00:00 /bin -> usr/bin
lrwxrwxrwx 1 root root     8 Jan 16 00:00 /sbin -> usr/sbin
dr-xr-xr-x 1 root root 20480 May 22 13:53 /usr/bin
lrwxrwxrwx 1 root root     3 May 22 06:47 /usr/sbin -> bin

The initrd should have the same structure. Could you check that?

ln_r /usr/bin/busybox "$_path" should end up creating the symlink inside /usr/bin even when path is /usr/sbin/sh.

@challvy

challvy commented May 25, 2026

Copy link
Copy Markdown
Author

@bdrung usr/sbin is different?

# lsinitrd /boot/initramfs-$(uname -r)kdump.img | grep -E "^[ld].*\s(bin|sbin|usr/bin|usr/sbin)$"
lrwxrwxrwx   1 root     root            7 May 25 22:21 bin -> usr/bin
lrwxrwxrwx   1 root     root            8 May 25 22:21 sbin -> usr/sbin
drwxr-xr-x   2 root     root            0 May 25 22:21 usr/bin
drwxr-xr-x   2 root     root            0 May 25 22:21 usr/sbin
lrwxrwxrwx root/root                 8 2026-05-25 22:23 squashfs-root/sbin -> usr/sbin
lrwxrwxrwx root/root                 3 2026-05-25 22:23 squashfs-root/usr/sbin -> bin
...

@bdrung

bdrung commented May 25, 2026

Copy link
Copy Markdown
Member

Then we found the issue. usr/bin should be a symlink to usr/sbin in the initrd.

@challvy

challvy commented May 26, 2026

Copy link
Copy Markdown
Author

Then we found the issue. usr/bin should be a symlink to usr/sbin in the initrd.

Do you mean "usr/sbin should be a symlink to bin in the initrd"?

https://discussion.fedoraproject.org/t/f42-change-proposal-unify-usr-bin-and-usr-sbin-system-wide

@challvy

challvy commented May 26, 2026

Copy link
Copy Markdown
Author

@devkontrol @bdrung
As a downstream user of Fedora(42~43), can the temporary workaround be like this?

echo 'DRACUT_PATH="/usr/bin /bin /usr/sbin /sbin"' > /etc/kdump/dracut.conf.d/fix-path-order.conf

@devkontrol

Copy link
Copy Markdown
Collaborator

@devkontrol @bdrung As a downstream user of Fedora(42~43), can the temporary workaround be like this?

echo 'DRACUT_PATH="/usr/bin /bin /usr/sbin /sbin"' > /etc/kdump/dracut.conf.d/fix-path-order.conf

This is the wrong repository for the Fedora (42~43) discussion. A better repository would be the Fedora packaging repository. I would however want a more permanent fix, if I am the Fedora maintainer (which I am not).

As I mentioned earlier, this workaround does not work for the upstream dracut version (for any distribution).

@bdrung

bdrung commented May 26, 2026

Copy link
Copy Markdown
Member

Yes. The issue is that your system has the /usr/sbin -> bin symlink, but the initrd does not. We need to figure out how that could happen to fix it properly.

@devkontrol devkontrol added the stale communication is stuck label Jun 15, 2026
@devkontrol
devkontrol marked this pull request as draft August 10, 2026 12:48
@devkontrol

Copy link
Copy Markdown
Collaborator

@prabhakarpujeri - seems you're on Fedora. Perhaps you can try to help with this PR/issue as well. Thank you !

@devkontrol

Copy link
Copy Markdown
Collaborator

#2603 seems like a better direction, we should consider closing this PR.

prabhakarpujeri pushed a commit to prabhakarpujeri/dracut that referenced this pull request Aug 17, 2026
The squash loader assembles its own root directory and creates
usr/bin, usr/sbin and usr/lib as plain directories, only symlinking
the top-level bin/sbin/lib to their usr counterparts.

On merged-usr hosts where /usr/sbin itself is a symlink (/usr/sbin ->
bin, e.g. Fedora 42+), binaries resolved on the host's unified tree
are installed into only one of the two split trees in the loader. In
particular the busybox module places its sh applet symlink at
usr/sbin/sh while /bin/sh == /usr/bin/sh stays missing, so the kernel
cannot execute the shebang of init-squash.sh:

    Run /init as init process
    Failed to execute /init (error -2)

This breaks the kdump squashfs image on Fedora 42+ (issue dracut-ng#2454).

Mirror the host layout instead: if the host directory is a symlink,
replicate the same symlink in the loader directory; otherwise create
a real directory as before. This is the root-cause fix for the
directory-layout problem identified in PR dracut-ng#2455, which proposed to
special-case only the busybox sh symlink.

Reproduced the failure on Fedora 44 (busybox, squashfs, qemu):
before the change the loader contains usr/sbin as a real directory
holding 'sh -> ../bin/busybox' with /usr/bin/sh absent, and booting
the image fails to execute /init with error -2. After the change the
loader has 'usr/sbin -> bin' and 'usr/bin/sh -> busybox', /init
executes, the squash image is mounted and the inner systemd initramfs
reaches initrd-switch-root.target.

Fixes: dracut-ng#2454
@devkontrol devkontrol closed this Aug 17, 2026
@challvy

challvy commented Aug 18, 2026

Copy link
Copy Markdown
Author

#2603 seems like a better direction, we should consider closing this PR.

OK. Thanks!

prabhakarpujeri pushed a commit to prabhakarpujeri/dracut that referenced this pull request Aug 19, 2026
The squash loader assembles its own root directory and creates
usr/bin, usr/sbin and usr/lib as plain directories, only symlinking
the top-level bin/sbin/lib to their usr counterparts.

On merged-usr hosts where /usr/sbin itself is a symlink (/usr/sbin ->
bin, e.g. Fedora 42+), binaries resolved on the host's unified tree
are installed into only one of the two split trees in the loader. In
particular the busybox module places its sh applet symlink at
usr/sbin/sh while /bin/sh == /usr/bin/sh stays missing, so the kernel
cannot execute the shebang of init-squash.sh:

    Run /init as init process
    Failed to execute /init (error -2)

This breaks the kdump squashfs image on Fedora 42+ (issue dracut-ng#2454).

Mirror the host layout instead: if the host directory is a symlink,
replicate the same symlink in the loader directory; otherwise create
a real directory as before. This is the root-cause fix for the
directory-layout problem identified in PR dracut-ng#2455, which proposed to
special-case only the busybox sh symlink.

Reproduced the failure on Fedora 44 (busybox, squashfs, qemu):
before the change the loader contains usr/sbin as a real directory
holding 'sh -> ../bin/busybox' with /usr/bin/sh absent, and booting
the image fails to execute /init with error -2. After the change the
loader has 'usr/sbin -> bin' and 'usr/bin/sh -> busybox', /init
executes, the squash image is mounted and the inner systemd initramfs
reaches initrd-switch-root.target.

Fixes: dracut-ng#2454
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

busybox Issues related to the busybox module modules Issue tracker for all modules stale communication is stuck

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(busybox): ensure /usr/bin/sh symlink is always created

3 participants