fix(busybox): ensure /usr/bin/sh symlink is always created #2454 - #2455
fix(busybox): ensure /usr/bin/sh symlink is always created #2454#2455challvy wants to merge 1 commit into
Conversation
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>
|
I also created a bugreport in https://bugzilla.redhat.com/show_bug.cgi?id=2481179 |
@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 |
| # 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 |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
@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?
|
The directory structure looks like this on Fedora: The initrd should have the same structure. Could you check that?
|
|
@bdrung usr/sbin is different? |
|
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 |
|
@devkontrol @bdrung |
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). |
|
Yes. The issue is that your system has the |
|
@prabhakarpujeri - seems you're on Fedora. Perhaps you can try to help with this PR/issue as well. Thank you ! |
|
#2603 seems like a better direction, we should consider closing this PR. |
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
OK. Thanks! |
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
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