bash gets built with a /bin/bash that is missing the executable bit
Bug Description
If I stage the bash and quilt packages in a part in a classic snap, then the snap gets built with a bin/bash file that is not executable. Subsequent attempts to use it fail.
To Reproduce
On a fresh Noble VM:
sudo snap install --classic snapcraft
# installs 8.4.3 (12823)
mkdir -p demo/snap
cat > demo/snap/snapcraft.yaml <<EOT
name: demo
version: 1
summary: demo
description: demo
confinement: classic
grade: devel
base: core24
apps:
demo:
command: usr/bin/bash
parts:
demo:
build-attributes: [enable-patchelf]
plugin: nil
stage-packages: [bash, quilt]
EOT
cd demo
snapcraft build --destructive-mode
Expected result: parts/demo/install/bin/bash is executable, and this passes through to the final snap (if you continue you should also see it in prime/bin/bash as executable, etc).
Actual result: parts/demo/install/bin/bash is not executable and this passes through to the final snap. Then scripts that try to use it fail.
Environment
Ubuntu 24.04 cloud image, snapcraft in destructive mode. Snapcraft snap 8.4.3 (12823).
snapcraft.yaml
name: demo
version: 1
summary: demo
description: demo
confinement: classic
grade: devel
base: core24
apps:
demo:
command: usr/bin/bash
parts:
demo:
build-attributes: [enable-patchelf]
plugin: nil
stage-packages: [bash, quilt]
Relevant log output
2024-10-13 13:12:26.879 Building demo
2024-10-13 13:12:26.893 execute action demo:Action(part_name='demo', step=Step.BUILD, action_type=ActionType.RUN, reason=None, project_vars=None, properties=ActionProperties(changed_files=None, changed_dirs=None))
2024-10-13 13:12:26.894 load state file: /home/ubuntu/demo/parts/demo/state/pull
2024-10-13 13:12:28.158 fix artifacts: unpack_dir='/home/ubuntu/demo/parts/demo/install'
2024-10-13 13:12:28.175 fix symlink: path='/home/ubuntu/demo/parts/demo/install/usr/share/quilt/compat/bash', unpack_dir='/home/ubuntu/demo/parts/demo/install', root='/home/ubuntu/demo/parts/demo/install/usr/share/quilt/compat'
2024-10-13 13:12:28.207 fix symlink: target='/home/ubuntu/demo/parts/demo/install/bin/bash'
2024-10-13 13:12:28.207 Copying needed target link from the system: /usr/bin/bash
2024-10-13 13:12:30.039 remove directory /home/ubuntu/demo/parts/demo/build
2024-10-13 13:12:30.041 Executing PosixPath('/home/ubuntu/demo/parts/demo/run/build.sh')
Additional context
The trace output suggests that snapcraft is doing something because usr/share/quilt/compat/bash is a symlink. If I drop quilt from stage-packages, then bin/bash isn't created.
I found this when trying to move the git-ubuntu snap to core24. That's complex enough so I came up with a minimal reproducer.
Workaround: I did this:
override-stage: |
craftctl default
chmod 755 bin/bash
This seems to be happening here: https://github.com/canonical/craft-parts/blob/5b9dffb363c90e77f6e4f448852688251ad5bd70/craft_parts/packages/normalize.py#L148-L157
Please note that since you're building a classic snap, the symlink in usr/share/quilt/compat/bash may be incorrect. Do you expect this to go to the system's bash, or the bash staged inside this snap? If the latter, you'll need to rewrite that symlink (probably in an override-build script) to point to ../../../bin/bash rather than /bin/bash - otherwise it'll execute the system's bash.
@mr-cal @cmatsuoka Should craft-parts be rewriting absolute symlinks like this when staging a package?
Do you expect this to go to the system's bash, or the bash staged inside this snap?
In quilt's case, I'd like for it to be entirely contained within the classic snap, and not touch the host system except to act on the source tree and patch series that it is given that will be on the host system. That suggests that the symlinks should be relative.
Of course it's tricky in the case of a classic snap where these sorts of things tend to be leaky. I'm not sure the symlinks are actually used. If they are, then that's probably wrong and I can close that leak, but there are probably others. I try to ensure that there's good test coverage (I know quilt is exercised), and then act when I have specific test failures. But sure, I can tweak this in override-build to make them relative if snapcraft can't do that for me in any other more abstract way.
The reason I ran into this was because in some other places I was using $SNAP/bin/bash and because I happened to stage-packages: [quilt], switching from core20 to core24 regressed that location. I know behaviour has changed since usrmerge. I've tried to eliminate use of that path now, favouring $SNAP/usr/bin/bash instead for unrelated reasons.