From 4bd6b9bb0a0f5af8893761e48d79f949cadf40fa Mon Sep 17 00:00:00 2001 From: Valentin David Date: Tue, 13 Nov 2018 21:24:20 +0100 Subject: Fix issue with absolute symbolic links in copy_files/link_files When paths given by caller for copy_files/link_files used absolute symbolic links, sorting the paths required to resolve links within the sysroot. --- buildstream/utils.py | 46 +++++++++++++++++++++- tests/integration/compose-symlinks.py | 17 ++++++++ .../elements/compose-symlinks/a-foo-symlink.bst | 11 ++++++ .../compose-symlinks/compose-absolute-symlink.bst | 14 +++++++ .../project/elements/compose-symlinks/foo-dir.bst | 11 ++++++ .../compose-symlinks/integration-move-dir.bst | 14 +++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/integration/project/elements/compose-symlinks/a-foo-symlink.bst create mode 100644 tests/integration/project/elements/compose-symlinks/compose-absolute-symlink.bst create mode 100644 tests/integration/project/elements/compose-symlinks/foo-dir.bst create mode 100644 tests/integration/project/elements/compose-symlinks/integration-move-dir.bst diff --git a/buildstream/utils.py b/buildstream/utils.py index ae5da34d7..fc496154e 100644 --- a/buildstream/utils.py +++ b/buildstream/utils.py @@ -787,6 +787,48 @@ def _ensure_real_directory(root, destpath): return destpath_resolved +@functools.lru_cache(maxsize=1) +def _symloop_max(): + if hasattr(os, 'sysconf'): + try: + ret = os.sysconf('_SC_SYMLOOP_MAX') + if ret != -1: + return ret + except ValueError: + pass + return 8 + + +@functools.lru_cache(maxsize=64) +def _sysroot_realpath(path, sysroot): + assert not os.path.isabs(path) + assert os.path.isabs(sysroot) + + loop_count = _symloop_max() + while True: + full_path = os.path.join(sysroot, path) + st = os.lstat(full_path) + mode = st.st_mode + if not stat.S_ISLNK(mode): + break + loop_count = loop_count - 1 + if loop_count < 0: + raise UtilError("Symlink loop detected: {}".format(os.path.join(sysroot, path))) + link_path = os.readlink(full_path) + if not os.path.isabs(link_path): + link_path = os.path.join('/', os.path.dirname(path), link_path) + path = os.path.relpath(os.path.normpath(link_path), '/') + + parent = os.path.dirname(path) + if parent != '': + parent = _sysroot_realpath(parent, sysroot) + full_parent = os.path.join(sysroot, parent) + if not os.path.isdir(full_parent): + raise UtilError("Path is not a directory: {}".format(full_parent)) + + return os.path.join(parent, os.path.basename(path)) + + # _process_list() # # Internal helper for copying/moving/linking file lists @@ -817,10 +859,10 @@ def _process_list(srcdir, destdir, filelist, actionfunc, result, # those directories. if not presorted: resolved = [] + _sysroot_realpath.cache_clear() for f in filelist: dirname = os.path.dirname(f) - dirname = os.path.realpath(os.path.join(srcdir, dirname)) - dirname = os.path.relpath(dirname, srcdir) + dirname = _sysroot_realpath(dirname, srcdir) if dirname == '.': resolved.append(os.path.basename(f)) else: diff --git a/tests/integration/compose-symlinks.py b/tests/integration/compose-symlinks.py index bf279fa6f..c72d81866 100644 --- a/tests/integration/compose-symlinks.py +++ b/tests/integration/compose-symlinks.py @@ -7,6 +7,7 @@ from buildstream import _yaml from tests.testutils import cli_integration as cli from tests.testutils.integration import walk_dir +from tests.testutils.site import IS_LINUX, HAVE_BWRAP pytestmark = pytest.mark.integration @@ -41,3 +42,19 @@ def test_compose_symlinks(cli, tmpdir, datafiles): assert set(walk_dir(checkout)) == set(['/sbin', '/usr', '/usr/sbin', '/usr/sbin/init', '/usr/sbin/dummy']) + + +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.skipif(not IS_LINUX or not HAVE_BWRAP, reason='Only available on linux with bubblewrap') +def test_compose_absolute_symlinks(cli, tmpdir, datafiles): + project = str(datafiles) + checkout = os.path.join(cli.directory, 'checkout') + element_path = os.path.join(project, 'elements') + + result = cli.run(project=project, args=['build', 'compose-symlinks/compose-absolute-symlink.bst']) + result.assert_success() + + result = cli.run(project=project, args=['checkout', 'compose-symlinks/compose-absolute-symlink.bst', checkout]) + result.assert_success() + + assert os.readlink(os.path.join(checkout, 'foo')) == 'test/foo' diff --git a/tests/integration/project/elements/compose-symlinks/a-foo-symlink.bst b/tests/integration/project/elements/compose-symlinks/a-foo-symlink.bst new file mode 100644 index 000000000..3beff338a --- /dev/null +++ b/tests/integration/project/elements/compose-symlinks/a-foo-symlink.bst @@ -0,0 +1,11 @@ +kind: script + +depends: +- filename: base.bst + type: build + +config: + commands: + - | + mkdir -p "%{install-root}/bar" + ln -s "/bar" "%{install-root}/foo" diff --git a/tests/integration/project/elements/compose-symlinks/compose-absolute-symlink.bst b/tests/integration/project/elements/compose-symlinks/compose-absolute-symlink.bst new file mode 100644 index 000000000..7111cb4c7 --- /dev/null +++ b/tests/integration/project/elements/compose-symlinks/compose-absolute-symlink.bst @@ -0,0 +1,14 @@ +kind: compose + +depends: +- filename: compose-symlinks/foo-dir.bst + type: build +- filename: compose-symlinks/a-foo-symlink.bst + type: build +- filename: compose-symlinks/integration-move-dir.bst + type: build + +config: + include-orphans: true + exclude: + - dummy diff --git a/tests/integration/project/elements/compose-symlinks/foo-dir.bst b/tests/integration/project/elements/compose-symlinks/foo-dir.bst new file mode 100644 index 000000000..e9206cd32 --- /dev/null +++ b/tests/integration/project/elements/compose-symlinks/foo-dir.bst @@ -0,0 +1,11 @@ +kind: script + +depends: +- filename: base.bst + type: build + +config: + commands: + - | + mkdir -p "%{install-root}/foo" + echo test >"%{install-root}/foo/foo.txt" diff --git a/tests/integration/project/elements/compose-symlinks/integration-move-dir.bst b/tests/integration/project/elements/compose-symlinks/integration-move-dir.bst new file mode 100644 index 000000000..851f03fa9 --- /dev/null +++ b/tests/integration/project/elements/compose-symlinks/integration-move-dir.bst @@ -0,0 +1,14 @@ +kind: stack + +depends: +- filename: base.bst + +public: + bst: + integration-commands: + - | + mkdir test + mv foo test/ + mv bar test/ + ln -s /test/foo /foo + ln -s /test/bar /bar -- cgit v1.2.1