reproduction: src_install() { dodir /foo dosym / /foo/sym dosym /xxx /foo/broken-sym touch "${D}/foo/file" mkdir "${D}"/foo/dir } then install it with INSTALL_MASK=/foo. the /foo/sym will be left behind. looks like it's due to os.walk (mis)behavior. symlinks are placed into dirs/files based on its target (probably using stat) instead of lstat. you can see: mkdir x ln -s / x/sym ln -s /xxx x/broken-sym touch s/file python >>> import os >>> for args in os.walk('./x'): ... print(args) ... ('./x', ['sym'], ['broken-sym', 'file']) i tried hacking up install_mask.py, but ran into failures in portage's internal os.unlink wrapper. so i gave up. @@ -173,4 +102,15 @@ def install_mask_dir(base_dir, install_m continue dir_stack.append(parent) + + # os.walk will return valid symlinks to dirs in dirs. + for d in dirs: + try: + tmpd = _unicode_decode(d, errors='strict') + except UnicodeDecodeError: + continue + abs_path = os.path.join(parent, tmpd) + if os.path.islink(abs_path): + files.append(d) + for fname in files: try: example: Traceback (most recent call last): File "/usr/lib64/python2.7/site-packages/portage/dbapi/_MergeProcess.py", line 234, in _spawn prev_mtimes=self.prev_mtimes, counter=counter) File "/usr/lib64/python2.7/site-packages/portage/dbapi/vartree.py", line 1677, in wrapper return f(self, *args, **kwargs) File "/usr/lib64/python2.7/site-packages/portage/dbapi/vartree.py", line 5167, in merge counter=counter) File "/usr/lib64/python2.7/site-packages/portage/dbapi/vartree.py", line 3888, in treewalk install_mask_dir(self.settings["ED"], install_mask) File "/usr/lib64/python2.7/site-packages/portage/util/install_mask.py", line 98, in install_mask_dir for parent, dirs, files in os.walk(base_dir, onerror=onerror): File "/usr/lib64/python2.7/os.py", line 296, in walk for x in walk(new_path, topdown, onerror, followlinks): File "/usr/lib64/python2.7/os.py", line 296, in walk for x in walk(new_path, topdown, onerror, followlinks): File "/usr/lib64/python2.7/os.py", line 281, in walk onerror(err) File "/usr/lib64/python2.7/site-packages/portage/util/install_mask.py", line 80, in _raise_exc raise wrapper FileNotFound: [Errno 2] No such file or directory: '/build/elm/tmp/portage/app-eselect/eselect-awk-0.2/image/foo/sym'
When adding a symlink to 'files' variable, you forgot to remove it from 'dirs' variable: if os.path.islink(abs_path): files.append(d) dirs.remove(d) Then your patch would happen to work.
ChromiumOS patch: https://chromium.googlesource.com/chromiumos/third_party/portage_tool/+/6473079d07351dda49a906cb89d24a9a39526bf7%5E%21/
The bug has been referenced in the following commit(s): https://gitweb.gentoo.org/proj/portage.git/commit/?id=5cc166489106da93c20ad3c5bb138ba8a359de2c commit 5cc166489106da93c20ad3c5bb138ba8a359de2c Author: Jeff Chase <jnchase@google.com> AuthorDate: 2021-05-04 17:29:58 +0000 Commit: Sam James <sam@gentoo.org> CommitDate: 2022-04-15 04:26:22 +0000 install_mask: remove masked symlinks to directories (bug 678462) os.walk() categorizes symlinks to directories as directories so they were being ignored by INSTALL_MASK. This change calls os.scandir() instead which efficiently provides more control. [sam: cherry-picked from chromiumos' third_party/portage_tool repo] (cherry picked from commit 6473079d07351dda49a906cb89d24a9a39526bf7) Bug: https://bugs.gentoo.org/678462 Signed-off-by: Sam James <sam@gentoo.org> Closes: https://github.com/gentoo/portage/pull/820 Signed-off-by: Sam James <sam@gentoo.org> lib/portage/tests/util/test_install_mask.py | 33 +++++++++++++++++++++++++++-- lib/portage/util/install_mask.py | 18 ++++++++++------ 2 files changed, 42 insertions(+), 9 deletions(-)