aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/portage/repository')
-rw-r--r--lib/portage/repository/config.py88
-rw-r--r--lib/portage/repository/meson.build10
-rw-r--r--lib/portage/repository/storage/hardlink_quarantine.py8
-rw-r--r--lib/portage/repository/storage/hardlink_rcu.py10
-rw-r--r--lib/portage/repository/storage/meson.build11
5 files changed, 98 insertions, 29 deletions
diff --git a/lib/portage/repository/config.py b/lib/portage/repository/config.py
index 0b591d94f..c9dfffa22 100644
--- a/lib/portage/repository/config.py
+++ b/lib/portage/repository/config.py
@@ -6,9 +6,11 @@ import io
import logging
import warnings
import re
+import shlex
import typing
import portage
+from pathlib import Path
from portage import eclass_cache, os
from portage.checksum import get_valid_checksum_keys
from portage.const import PORTAGE_BASE_PATH, REPO_NAME_LOC, USER_CONFIG_PATH
@@ -20,7 +22,6 @@ from portage.env.loaders import KeyValuePairFileLoader
from portage.util import (
normalize_path,
read_corresponding_eapi_file,
- shlex_split,
stack_lists,
writemsg,
writemsg_level,
@@ -160,6 +161,7 @@ class RepoConfig:
"thin_manifest",
"update_changelog",
"user_location",
+ "volatile",
"_eapis_banned",
"_eapis_deprecated",
"_masters_orig",
@@ -330,10 +332,45 @@ class RepoConfig:
self.name = name
if portage._sync_mode:
missing = False
-
elif name == "DEFAULT":
missing = False
+ volatile = repo_opts.get("volatile")
+ # If volatile is explicitly set, go with it.
+ if volatile is not None:
+ self.volatile = volatile in ("true", "yes")
+ else:
+ # If it's unset, we default to no (i.e. the repository is not volatile),
+ # but with a heuristic for when a repository is not likely to be suitable
+ # (likely to contain custom user changes).
+ try:
+ # If the repository doesn't exist, we can't check its ownership,
+ # so err on the safe side.
+ if missing or not self.location:
+ self.volatile = True
+ # On Prefix, you can't rely on the ownership as a proxy for user
+ # owned because the user typically owns everything.
+ # But we can't access if we're on Prefix here, so use whether
+ # we're under /var/db/repos instead.
+ elif not self.location.startswith("/var/db/repos"):
+ self.volatile = True
+ # If the owner of the repository isn't root or Portage, it's
+ # an indication the user may expect to be able to safely make
+ # changes in the directory, so default to volatile.
+ elif Path(self.location).owner() not in ("root", "portage"):
+ self.volatile = True
+ else:
+ self.volatile = False
+ except Exception:
+ # There's too many conditions here to refine the exception list:
+ # - We lack permissions to poke at the directory (PermissionError)
+ # - Its UID doesn't actually exist and the repository
+ # won't be synced by the user (KeyError).
+ # - The directory doesn't exist (FileNotFoundError)
+ # - Probably many others.
+ # So, just fail safe.
+ self.volatile = True
+
self.eapi = None
self.missing_repo_name = missing
# sign_commit is disabled by default, since it requires Git >=1.7.9,
@@ -531,16 +568,15 @@ class RepoConfig:
repo_name_path = os.path.join(repo_path, REPO_NAME_LOC)
f = None
try:
- f = io.open(
+ f = open(
_unicode_encode(
repo_name_path, encoding=_encodings["fs"], errors="strict"
),
- mode="r",
encoding=_encodings["repo.content"],
errors="replace",
)
return f.readline().strip(), False
- except EnvironmentError:
+ except OSError:
return "x-" + os.path.basename(repo_path), True
finally:
if f is not None:
@@ -548,7 +584,7 @@ class RepoConfig:
def info_string(self):
"""
- Returns a formatted string containing informations about the repository.
+ Returns a formatted string containing information about the repository.
Used by emerge --info.
"""
indent = " " * 4
@@ -582,6 +618,8 @@ class RepoConfig:
repo_msg.append(
indent + "eclass-overrides: " + " ".join(self.eclass_overrides)
)
+ if self.volatile is not None:
+ repo_msg.append(indent + "volatile: " + str(self.volatile))
for o, v in self.module_specific_options.items():
if v is not None:
repo_msg.append(indent + o + ": " + v)
@@ -589,16 +627,18 @@ class RepoConfig:
return "\n".join(repo_msg)
def __repr__(self):
- return "<portage.repository.config.RepoConfig(name=%r, location=%r)>" % (
- self.name,
- _unicode_decode(self.location),
+ return (
+ "<portage.repository.config.RepoConfig(name={!r}, location={!r})>".format(
+ self.name,
+ _unicode_decode(self.location),
+ )
)
def __str__(self):
d = {}
for k in self.__slots__:
d[k] = getattr(self, k, None)
- return "%s" % (d,)
+ return f"{d}"
class RepoConfigLoader:
@@ -616,7 +656,7 @@ class RepoConfigLoader:
portdir_orig = portdir
overlays.append(portdir)
try:
- port_ov = [normalize_path(i) for i in shlex_split(portdir_overlay)]
+ port_ov = [normalize_path(i) for i in shlex.split(portdir_overlay)]
except ValueError as e:
# File "/usr/lib/python3.2/shlex.py", line 168, in read_token
# raise ValueError("No closing quotation")
@@ -689,8 +729,16 @@ class RepoConfigLoader:
"sync_umask",
"sync_uri",
"sync_user",
+ "volatile",
):
v = getattr(repos_conf_opts, k, None)
+
+ # If PORTDIR_OVERLAY is set, we have to require volatile,
+ # because it'll break changes e.g. with ebuild(1) or
+ # development in a local repository with the same repo_name.
+ if k == "volatile" and portdir_overlay:
+ v = True
+
if v is not None:
setattr(repo, k, v)
@@ -721,7 +769,6 @@ class RepoConfigLoader:
prepos[repo.name] = repo
else:
-
if not portage._sync_mode:
writemsg(
_("!!! Invalid PORTDIR_OVERLAY (not a dir): '%s'\n") % ov,
@@ -1143,7 +1190,7 @@ class RepoConfigLoader:
def _check_locations(self):
"""Check if repositories location are correct and show a warning message if not"""
- for (name, r) in self.prepos.items():
+ for name, r in self.prepos.items():
if name != "DEFAULT":
if r.location is None:
writemsg(
@@ -1215,6 +1262,7 @@ class RepoConfigLoader:
"sync_allow_hardlinks",
"sync_openpgp_key_refresh",
"sync_rcu",
+ "volatile",
)
str_or_int_keys = (
"auto_sync",
@@ -1252,33 +1300,33 @@ class RepoConfigLoader:
):
if repo_name != repo.name:
continue
- config_string += "\n[%s]\n" % repo_name
+ config_string += f"\n[{repo_name}]\n"
for key in sorted(keys):
if key == "main_repo" and repo_name != "DEFAULT":
continue
if getattr(repo, key) is not None:
if key in bool_keys:
- config_string += "%s = %s\n" % (
+ config_string += "{} = {}\n".format(
key.replace("_", "-"),
"true" if getattr(repo, key) else "false",
)
elif key in str_or_int_keys:
- config_string += "%s = %s\n" % (
+ config_string += "{} = {}\n".format(
key.replace("_", "-"),
getattr(repo, key),
)
elif key in str_tuple_keys:
- config_string += "%s = %s\n" % (
+ config_string += "{} = {}\n".format(
key.replace("_", "-"),
" ".join(getattr(repo, key)),
)
elif key in repo_config_tuple_keys:
- config_string += "%s = %s\n" % (
+ config_string += "{} = {}\n".format(
key.replace("_", "-"),
" ".join(x.name for x in getattr(repo, key)),
)
for o, v in repo.module_specific_options.items():
- config_string += "%s = %s\n" % (o, v)
+ config_string += f"{o} = {v}\n"
return config_string.lstrip("\n")
@@ -1331,7 +1379,7 @@ def parse_layout_conf(repo_location, repo_name=None):
data = {}
- # None indicates abscence of a masters setting, which later code uses
+ # None indicates absence of a masters setting, which later code uses
# to trigger a backward compatibility fallback that sets an implicit
# master. In order to avoid this fallback behavior, layout.conf can
# explicitly set masters to an empty value, which will result in an
diff --git a/lib/portage/repository/meson.build b/lib/portage/repository/meson.build
new file mode 100644
index 000000000..ecc71a6fa
--- /dev/null
+++ b/lib/portage/repository/meson.build
@@ -0,0 +1,10 @@
+py.install_sources(
+ [
+ 'config.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/repository',
+ pure : not native_extensions
+)
+
+subdir('storage')
diff --git a/lib/portage/repository/storage/hardlink_quarantine.py b/lib/portage/repository/storage/hardlink_quarantine.py
index ad9e64bcc..3fb1396fa 100644
--- a/lib/portage/repository/storage/hardlink_quarantine.py
+++ b/lib/portage/repository/storage/hardlink_quarantine.py
@@ -39,7 +39,7 @@ class HardlinkQuarantineRepoStorage(RepoStorageInterface):
"""
Run cmd and raise RepoStorageException on failure.
- @param cmd: command to executre
+ @param cmd: command to execute
@type cmd: list
"""
p = SpawnProcess(
@@ -48,7 +48,7 @@ class HardlinkQuarantineRepoStorage(RepoStorageInterface):
p.start()
if await p.async_wait() != os.EX_OK:
raise RepoStorageException(
- "command exited with status {}: {}".format(p.returncode, " ".join(cmd))
+ f"command exited with status {p.returncode}: {' '.join(cmd)}"
)
async def init_update(self):
@@ -70,7 +70,7 @@ class HardlinkQuarantineRepoStorage(RepoStorageInterface):
"--exclude=/lost+found",
"--exclude=/packages",
"--exclude",
- "/{}".format(os.path.basename(update_location)),
+ f"/{os.path.basename(update_location)}",
self._user_location + "/",
update_location + "/",
]
@@ -99,7 +99,7 @@ class HardlinkQuarantineRepoStorage(RepoStorageInterface):
"--exclude=/lost+found",
"--exclude=/packages",
"--exclude",
- "/{}".format(os.path.basename(update_location)),
+ f"/{os.path.basename(update_location)}",
update_location + "/",
self._user_location + "/",
]
diff --git a/lib/portage/repository/storage/hardlink_rcu.py b/lib/portage/repository/storage/hardlink_rcu.py
index 4fd87a24b..6f464c8d0 100644
--- a/lib/portage/repository/storage/hardlink_rcu.py
+++ b/lib/portage/repository/storage/hardlink_rcu.py
@@ -19,13 +19,13 @@ class HardlinkRcuRepoStorage(RepoStorageInterface):
Enable read-copy-update (RCU) behavior for sync operations. The
current latest immutable version of a repository will be
reference by a symlink found where the repository would normally
- be located. Repository consumers should resolve the cannonical
+ be located. Repository consumers should resolve the canonical
path of this symlink before attempt to access the repository,
and all operations should be read-only, since the repository
is considered immutable. Updates occur by atomic replacement
of the symlink, which causes new consumers to use the new
immutable version, while any earlier consumers continue to use
- the cannonical path that was resolved earlier.
+ the canonical path that was resolved earlier.
Performance is better than HardlinkQuarantineRepoStorage,
since commit involves atomic replacement of a symlink. Since
@@ -112,7 +112,7 @@ class HardlinkRcuRepoStorage(RepoStorageInterface):
"""
Run cmd and raise RepoStorageException on failure.
- @param cmd: command to executre
+ @param cmd: command to execute
@type cmd: list
@param privileged: run with maximum privileges
@type privileged: bool
@@ -125,7 +125,7 @@ class HardlinkRcuRepoStorage(RepoStorageInterface):
p.start()
if await p.async_wait() != os.EX_OK:
raise RepoStorageException(
- "command exited with status {}: {}".format(p.returncode, " ".join(cmd))
+ f"command exited with status {p.returncode}: {' '.join(cmd)}"
)
async def init_update(self):
@@ -216,7 +216,7 @@ class HardlinkRcuRepoStorage(RepoStorageInterface):
os.unlink(new_symlink)
except OSError:
pass
- os.symlink("snapshots/{}".format(new_id), new_symlink)
+ os.symlink(f"snapshots/{new_id}", new_symlink)
# If SyncManager.pre_sync creates an empty directory where
# self._latest_symlink is suppose to be (which is normal if
diff --git a/lib/portage/repository/storage/meson.build b/lib/portage/repository/storage/meson.build
new file mode 100644
index 000000000..1845f9f51
--- /dev/null
+++ b/lib/portage/repository/storage/meson.build
@@ -0,0 +1,11 @@
+py.install_sources(
+ [
+ 'hardlink_quarantine.py',
+ 'hardlink_rcu.py',
+ 'inplace.py',
+ 'interface.py',
+ '__init__.py',
+ ],
+ subdir : 'portage/repository/storage',
+ pure : not native_extensions
+)