diff -urN portage-3.0.82-orig/bin/dispatch-conf portage-3.0.82/bin/dispatch-conf --- portage-3.0.82-orig/bin/dispatch-conf 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/bin/dispatch-conf 2026-08-24 01:19:06 +0900 @@ -215,6 +215,7 @@ config_root, config_paths, portage.settings.get("CONFIG_PROTECT_MASK", "").split(), + portage.settings.get("CONFIG_PROTECT_UNMASK", "").split(), case_insensitive=("case-insensitive-fs" in portage.settings.features), ) diff -urN portage-3.0.82-orig/bin/portageq portage-3.0.82/bin/portageq --- portage-3.0.82-orig/bin/portageq 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/bin/portageq 2026-08-24 01:19:06 +0900 @@ -421,10 +421,12 @@ settings = portage.settings protect = settings.get("CONFIG_PROTECT", "").split() protect_mask = settings.get("CONFIG_PROTECT_MASK", "").split() + protect_unmask = settings.get("CONFIG_PROTECT_UNMASK", "").split() protect_obj = ConfigProtect( root, protect, protect_mask, + protect_unmask, case_insensitive=("case-insensitive-fs" in settings.features), ) if protect_obj.isprotected(f): @@ -458,10 +460,12 @@ settings = portage.settings protect = settings.get("CONFIG_PROTECT", "").split() protect_mask = settings.get("CONFIG_PROTECT_MASK", "").split() + protect_unmask = settings.get("CONFIG_PROTECT_UNMASK", "").split() protect_obj = ConfigProtect( root, protect, protect_mask, + protect_unmask, case_insensitive=("case-insensitive-fs" in settings.features), ) diff -urN portage-3.0.82-orig/lib/_emerge/actions.py portage-3.0.82/lib/_emerge/actions.py --- portage-3.0.82-orig/lib/_emerge/actions.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/_emerge/actions.py 2026-08-24 01:20:39 +0900 @@ -2201,6 +2201,7 @@ "GENTOO_MIRRORS", "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", + "CONFIG_PROTECT_UNMASK", "DISTDIR", "ENV_UNSET", "PKGDIR", diff -urN portage-3.0.82-orig/lib/_emerge/depgraph.py portage-3.0.82/lib/_emerge/depgraph.py --- portage-3.0.82-orig/lib/_emerge/depgraph.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/_emerge/depgraph.py 2026-08-24 01:19:06 +0900 @@ -10983,6 +10983,7 @@ settings["PORTAGE_CONFIGROOT"], settings.get("CONFIG_PROTECT", "").split(), settings.get("CONFIG_PROTECT_MASK", "").split(), + settings.get("CONFIG_PROTECT_UNMASK", "").split(), case_insensitive=("case-insensitive-fs" in settings.features), ) diff -urN portage-3.0.82-orig/lib/portage/_global_updates.py portage-3.0.82/lib/portage/_global_updates.py --- portage-3.0.82-orig/lib/portage/_global_updates.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/_global_updates.py 2026-08-24 01:19:06 +0900 @@ -236,6 +236,7 @@ root, mysettings.get("CONFIG_PROTECT", "").split(), mysettings.get("CONFIG_PROTECT_MASK", "").split(), + mysettings.get("CONFIG_PROTECT_UNMASK", "").split(), repo_map, match_callback=_config_repo_match, case_insensitive="case-insensitive-fs" in mysettings.features, diff -urN portage-3.0.82-orig/lib/portage/const.py portage-3.0.82/lib/portage/const.py --- portage-3.0.82-orig/lib/portage/const.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/const.py 2026-08-24 01:19:06 +0900 @@ -126,6 +126,7 @@ "ACCEPT_KEYWORDS", "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", + "CONFIG_PROTECT_UNMASK", "ENV_UNSET", "FEATURES", "IUSE_IMPLICIT", diff -urN portage-3.0.82-orig/lib/portage/dbapi/bintree.py portage-3.0.82/lib/portage/dbapi/bintree.py --- portage-3.0.82-orig/lib/portage/dbapi/bintree.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/dbapi/bintree.py 2026-08-24 01:19:06 +0900 @@ -596,6 +596,7 @@ "CBUILD", "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", + "CONFIG_PROTECT_UNMASK", "FEATURES", "GENTOO_MIRRORS", "INSTALL_MASK", diff -urN portage-3.0.82-orig/lib/portage/dbapi/vartree.py portage-3.0.82/lib/portage/dbapi/vartree.py --- portage-3.0.82-orig/lib/portage/dbapi/vartree.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/dbapi/vartree.py 2026-08-24 01:19:06 +0900 @@ -2066,6 +2066,7 @@ self._eroot, self.settings.get("CONFIG_PROTECT", "").split(), self.settings.get("CONFIG_PROTECT_MASK", "").split(), + self.settings.get("CONFIG_PROTECT_UNMASK", "").split(), case_insensitive=("case-insensitive-fs" in self.settings.features), ) @@ -2346,6 +2347,7 @@ settings["EROOT"], settings.get("CONFIG_PROTECT", "").split(), settings.get("CONFIG_PROTECT_MASK", "").split(), + settings.get("CONFIG_PROTECT_UNMASK", "").split(), case_insensitive=("case-insensitive-fs" in settings.features), ) diff -urN portage-3.0.82-orig/lib/portage/package/ebuild/_config/special_env_vars.py portage-3.0.82/lib/portage/package/ebuild/_config/special_env_vars.py --- portage-3.0.82-orig/lib/portage/package/ebuild/_config/special_env_vars.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/package/ebuild/_config/special_env_vars.py 2026-08-24 01:19:06 +0900 @@ -283,6 +283,7 @@ "COLLISION_IGNORE", "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", + "CONFIG_PROTECT_UNMASK", "EGENCACHE_DEFAULT_OPTS", "EMERGE_DEFAULT_OPTS", "EMERGE_LOG_DIR", @@ -351,6 +352,7 @@ global_only_vars = frozenset( [ "CONFIG_PROTECT", + "CONFIG_PROTECT_UNMASK", ] ) diff -urN portage-3.0.82-orig/lib/portage/package/ebuild/config.py portage-3.0.82/lib/portage/package/ebuild/config.py --- portage-3.0.82-orig/lib/portage/package/ebuild/config.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/package/ebuild/config.py 2026-08-24 01:19:06 +0900 @@ -1177,6 +1177,7 @@ eroot_only_variables = ( "CONFIG_PROTECT", "CONFIG_PROTECT_MASK", + "CONFIG_PROTECT_UNMASK", "INFODIR", "INFOPATH", "MANPATH", diff -urN portage-3.0.82-orig/lib/portage/update.py portage-3.0.82/lib/portage/update.py --- portage-3.0.82-orig/lib/portage/update.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/update.py 2026-08-24 01:19:06 +0900 @@ -269,6 +269,7 @@ config_root, protect, protect_mask, + protect_unmask, update_iter, match_callback=None, case_insensitive=False, @@ -278,6 +279,7 @@ config_root - location of files to update protect - list of paths from CONFIG_PROTECT protect_mask - list of paths from CONFIG_PROTECT_MASK + protect_unmask - list of paths from CONFIG_PROTECT_UNMASK update_iter - list of update commands as returned from parse_updates(), or dict of {repo_name: list} match_callback - a callback which will be called with three arguments: @@ -424,7 +426,7 @@ sys.stdout.flush() protect_obj = ConfigProtect( - config_root, protect, protect_mask, case_insensitive=case_insensitive + config_root, protect, protect_mask, protect_unmask, case_insensitive=case_insensitive ) for x in update_files: updating_file = os.path.join(abs_user_config, x) diff -urN portage-3.0.82-orig/lib/portage/util/__init__.py portage-3.0.82/lib/portage/util/__init__.py --- portage-3.0.82-orig/lib/portage/util/__init__.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/util/__init__.py 2026-08-24 01:19:06 +0900 @@ -1721,10 +1721,11 @@ class ConfigProtect: - def __init__(self, myroot, protect_list, mask_list, case_insensitive=False): + def __init__(self, myroot, protect_list, mask_list, unmask_list, case_insensitive=False): self.myroot = myroot self.protect_list = protect_list self.mask_list = mask_list + self.unmask_list = unmask_list self.case_insensitive = case_insensitive self.updateprotect() @@ -1764,6 +1765,17 @@ # If it doesn't exist, there's no need to mask it. pass + self.protectunmask = [] + for x in self.unmask_list: + ppath = normalize_path(os.path.join(self.myroot, x.lstrip(os.path.sep))) + if os.path.isdir(os.path.dirname(ppath)): + self.protectunmask.append(ppath) + try: + if stat.S_ISDIR(os.stat(ppath).st_mode): + self._dirs.add(ppath) + except OSError: + pass + def isprotected(self, obj): """Returns True if obj is protected, False otherwise. The caller must ensure that obj is normalized with a single leading slash. A trailing @@ -1797,7 +1809,17 @@ continue # skip, it's in the mask masked = len(pmpath) - return protected > masked + if protected > masked: + return True + for pupath in self.protectunmask: + if obj == pupath: + # target is unmasked by exact match, treat as protected + return True + if pupath in self._dirs: + if obj.startswith(pupath + sep): + # target is in unmasked dir, treat as protected + return True + return False def new_protect_filename(mydest, newmd5=None, force=False): diff -urN portage-3.0.82-orig/lib/portage/util/env_update.py portage-3.0.82/lib/portage/util/env_update.py --- portage-3.0.82-orig/lib/portage/util/env_update.py 2026-08-22 14:02:19 +0900 +++ portage-3.0.82/lib/portage/util/env_update.py 2026-08-24 01:19:06 +0900 @@ -120,7 +120,7 @@ fns = templist del templist - space_separated = {"CONFIG_PROTECT", "CONFIG_PROTECT_MASK"} + space_separated = {"CONFIG_PROTECT", "CONFIG_PROTECT_MASK", "CONFIG_PROTECT_UNMASK"} colon_separated = { "ADA_INCLUDE_PATH", "ADA_OBJECTS_PATH",