sbang: respect package permissive package permissions for sbang (#25764)

Co-authored-by: Todd Gamblin <tgamblin@llnl.gov>
Co-authored-by: Tamara Dahlgren <35777542+tldahlgren@users.noreply.github.com>
This commit is contained in:
Paul Spencer 2021-12-18 23:07:20 -07:00 committed by Massimiliano Culpo
parent 17edf1ae90
commit e1cc28a30a
2 changed files with 96 additions and 12 deletions

View File

@ -4,6 +4,7 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import filecmp
import grp
import os
import re
import shutil
@ -14,7 +15,9 @@
import llnl.util.filesystem as fs
import llnl.util.tty as tty
import spack.package_prefs
import spack.paths
import spack.spec
import spack.store
#: OS-imposed character limit for shebang line: 127 for Linux; 511 for Mac.
@ -187,12 +190,48 @@ def install_sbang():
spack.paths.sbang_script, sbang_path):
return
# make $install_tree/bin and copy in a new version of sbang if needed
# make $install_tree/bin
sbang_bin_dir = os.path.dirname(sbang_path)
fs.mkdirp(sbang_bin_dir)
fs.install(spack.paths.sbang_script, sbang_path)
# get permissions for bin dir from configuration files
group_name = spack.package_prefs.get_package_group(spack.spec.Spec("all"))
config_mode = spack.package_prefs.get_package_dir_permissions(
spack.spec.Spec("all")
)
if group_name:
os.chmod(sbang_bin_dir, config_mode) # Use package directory permissions
else:
fs.set_install_permissions(sbang_bin_dir)
# set group on sbang_bin_dir if not already set (only if set in configuration)
if group_name and grp.getgrgid(os.stat(sbang_bin_dir).st_gid).gr_name != group_name:
os.chown(
sbang_bin_dir,
os.stat(sbang_bin_dir).st_uid,
grp.getgrnam(group_name).gr_gid
)
# copy over the fresh copy of `sbang`
sbang_tmp_path = os.path.join(
os.path.dirname(sbang_path),
".%s.tmp" % os.path.basename(sbang_path),
)
shutil.copy(spack.paths.sbang_script, sbang_tmp_path)
# set permissions on `sbang` (including group if set in configuration)
os.chmod(sbang_tmp_path, config_mode)
if group_name:
os.chown(
sbang_tmp_path,
os.stat(sbang_tmp_path).st_uid,
grp.getgrnam(group_name).gr_gid
)
# Finally, move the new `sbang` into place atomically
os.rename(sbang_tmp_path, sbang_path)
def post_install(spec):
"""This hook edits scripts so that they call /bin/bash

View File

@ -7,6 +7,7 @@
Test that Spack's shebang filtering works correctly.
"""
import filecmp
import grp
import os
import shutil
import stat
@ -19,6 +20,7 @@
import spack.hooks.sbang as sbang
import spack.paths
import spack.store
import spack.util.spack_yaml as syaml
from spack.util.executable import which
too_long = sbang.system_shebang_limit + 1
@ -256,7 +258,34 @@ def test_shebang_handles_non_writable_files(script_dir, sbang_line):
assert oct(not_writable_mode) == oct(st.st_mode)
def check_sbang_installation():
@pytest.fixture(scope='function')
def configure_group_perms():
conf = syaml.load_config("""\
all:
permissions:
read: world
write: group
group: {0}
""".format(grp.getgrgid(os.getegid()).gr_name))
spack.config.set('packages', conf, scope='user')
yield
@pytest.fixture(scope='function')
def configure_user_perms():
conf = syaml.load_config("""\
all:
permissions:
read: world
write: user
""")
spack.config.set('packages', conf, scope='user')
yield
def check_sbang_installation(group=False):
sbang_path = sbang.sbang_install_path()
sbang_bin_dir = os.path.dirname(sbang_path)
assert sbang_path.startswith(spack.store.store.unpadded_root)
@ -264,14 +293,22 @@ def check_sbang_installation():
assert os.path.exists(sbang_path)
assert fs.is_exe(sbang_path)
status = os.stat(sbang_path)
assert (status.st_mode & 0o777) == 0o755
status = os.stat(sbang_bin_dir)
assert (status.st_mode & 0o777) == 0o755
mode = (status.st_mode & 0o777)
if group:
assert mode == 0o775, 'Unexpected {0}'.format(oct(mode))
else:
assert mode == 0o755, 'Unexpected {0}'.format(oct(mode))
status = os.stat(sbang_path)
mode = (status.st_mode & 0o777)
if group:
assert mode == 0o775, 'Unexpected {0}'.format(oct(mode))
else:
assert mode == 0o755, 'Unexpected {0}'.format(oct(mode))
def test_install_sbang(install_mockery):
def run_test_install_sbang(group):
sbang_path = sbang.sbang_install_path()
sbang_bin_dir = os.path.dirname(sbang_path)
@ -279,7 +316,7 @@ def test_install_sbang(install_mockery):
assert not os.path.exists(sbang_bin_dir)
sbang.install_sbang()
check_sbang_installation()
check_sbang_installation(group)
# put an invalid file in for sbang
fs.mkdirp(sbang_bin_dir)
@ -287,11 +324,19 @@ def test_install_sbang(install_mockery):
f.write("foo")
sbang.install_sbang()
check_sbang_installation()
check_sbang_installation(group)
# install again and make sure sbang is still fine
sbang.install_sbang()
check_sbang_installation()
check_sbang_installation(group)
def test_install_group_sbang(install_mockery, configure_group_perms):
run_test_install_sbang(True)
def test_install_user_sbang(install_mockery, configure_user_perms):
run_test_install_sbang(False)
def test_install_sbang_too_long(tmpdir):