summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/open
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix/src/open
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/open')
-rw-r--r--vendor/gix/src/open/mod.rs67
-rw-r--r--vendor/gix/src/open/options.rs180
-rw-r--r--vendor/gix/src/open/repository.rs345
3 files changed, 592 insertions, 0 deletions
diff --git a/vendor/gix/src/open/mod.rs b/vendor/gix/src/open/mod.rs
new file mode 100644
index 000000000..77018f5a2
--- /dev/null
+++ b/vendor/gix/src/open/mod.rs
@@ -0,0 +1,67 @@
+use std::path::PathBuf;
+
+use crate::{bstr::BString, config, permission, Permissions};
+
+/// The options used in [`ThreadSafeRepository::open_opts()`][crate::ThreadSafeRepository::open_opts()].
+///
+/// ### Replacement Objects for the object database
+///
+/// The environment variables `GIT_REPLACE_REF_BASE` and `GIT_NO_REPLACE_OBJECTS` are mapped to `gitoxide.objects.replaceRefBase`
+/// and `gitoxide.objects.noReplace` respectively and then interpreted exactly as their environment variable counterparts.
+///
+/// Use [Permissions] to control which environment variables can be read, and config-overrides to control these values programmatically.
+#[derive(Clone)]
+pub struct Options {
+ pub(crate) object_store_slots: gix_odb::store::init::Slots,
+ /// Define what is allowed while opening a repository.
+ pub permissions: Permissions,
+ pub(crate) git_dir_trust: Option<gix_sec::Trust>,
+ /// Warning: this one is copied to to config::Cache - don't change it after repo open or keep in sync.
+ pub(crate) filter_config_section: Option<fn(&gix_config::file::Metadata) -> bool>,
+ pub(crate) lossy_config: Option<bool>,
+ pub(crate) lenient_config: bool,
+ pub(crate) bail_if_untrusted: bool,
+ pub(crate) api_config_overrides: Vec<BString>,
+ pub(crate) cli_config_overrides: Vec<BString>,
+ pub(crate) open_path_as_is: bool,
+ /// Internal to pass an already obtained CWD on to where it may also be used. This avoids the CWD being queried more than once per repo.
+ pub(crate) current_dir: Option<PathBuf>,
+}
+
+/// The error returned by [`crate::open()`].
+#[derive(Debug, thiserror::Error)]
+#[allow(missing_docs)]
+pub enum Error {
+ #[error("Failed to load the git configuration")]
+ Config(#[from] config::Error),
+ #[error("\"{path}\" does not appear to be a git repository")]
+ NotARepository {
+ source: gix_discover::is_git::Error,
+ path: PathBuf,
+ },
+ #[error(transparent)]
+ Io(#[from] std::io::Error),
+ #[error("The git directory at '{}' is considered unsafe as it's not owned by the current user.", .path.display())]
+ UnsafeGitDir { path: PathBuf },
+ #[error(transparent)]
+ EnvironmentAccessDenied(#[from] permission::env_var::resource::Error),
+}
+
+mod options;
+
+mod repository;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn size_of_options() {
+ let actual = std::mem::size_of::<Options>();
+ let limit = 160;
+ assert!(
+ actual <= limit,
+ "{actual} <= {limit}: size shouldn't change without us knowing (on windows, it's bigger)"
+ );
+ }
+}
diff --git a/vendor/gix/src/open/options.rs b/vendor/gix/src/open/options.rs
new file mode 100644
index 000000000..fb648e3c2
--- /dev/null
+++ b/vendor/gix/src/open/options.rs
@@ -0,0 +1,180 @@
+use std::path::PathBuf;
+
+use super::{Error, Options};
+use crate::{bstr::BString, config, Permissions, ThreadSafeRepository};
+
+impl Default for Options {
+ fn default() -> Self {
+ Options {
+ object_store_slots: Default::default(),
+ permissions: Default::default(),
+ git_dir_trust: None,
+ filter_config_section: None,
+ lossy_config: None,
+ lenient_config: true,
+ bail_if_untrusted: false,
+ open_path_as_is: false,
+ api_config_overrides: Vec::new(),
+ cli_config_overrides: Vec::new(),
+ current_dir: None,
+ }
+ }
+}
+
+/// Instantiation
+impl Options {
+ /// Options configured to prevent accessing anything else than the repository configuration file, prohibiting
+ /// accessing the environment or spreading beyond the git repository location.
+ pub fn isolated() -> Self {
+ Options::default().permissions(Permissions::isolated())
+ }
+}
+
+/// Generic modification
+impl Options {
+ /// An adapter to allow calling any builder method on this instance despite only having a mutable reference.
+ pub fn modify(&mut self, f: impl FnOnce(Self) -> Self) {
+ *self = f(std::mem::take(self));
+ }
+}
+
+/// Builder methods
+impl Options {
+ /// Apply the given configuration `values` like `init.defaultBranch=special` or `core.bool-implicit-true` in memory to as early
+ /// as the configuration is initialized to allow affecting the repository instantiation phase, both on disk or when opening.
+ /// The configuration is marked with [source API][gix_config::Source::Api].
+ pub fn config_overrides(mut self, values: impl IntoIterator<Item = impl Into<BString>>) -> Self {
+ self.api_config_overrides = values.into_iter().map(Into::into).collect();
+ self
+ }
+
+ /// Set configuration values of the form `core.abbrev=5` or `remote.origin.url = foo` or `core.bool-implicit-true` for application
+ /// as CLI overrides to the repository configuration, marked with [source CLI][gix_config::Source::Cli].
+ /// These are equivalent to CLI overrides passed with `-c` in `git`, for example.
+ pub fn cli_overrides(mut self, values: impl IntoIterator<Item = impl Into<BString>>) -> Self {
+ self.cli_config_overrides = values.into_iter().map(Into::into).collect();
+ self
+ }
+
+ /// Set the amount of slots to use for the object database. It's a value that doesn't need changes on the client, typically,
+ /// but should be controlled on the server.
+ pub fn object_store_slots(mut self, slots: gix_odb::store::init::Slots) -> Self {
+ self.object_store_slots = slots;
+ self
+ }
+
+ // TODO: tests
+ /// Set the given permissions, which are typically derived by a `Trust` level.
+ pub fn permissions(mut self, permissions: Permissions) -> Self {
+ self.permissions = permissions;
+ self
+ }
+
+ /// If `true`, default `false`, we will not modify the incoming path to open to assure it is a `.git` directory.
+ ///
+ /// If `false`, we will try to open the input directory as is, even though it doesn't appear to be a `git` repository
+ /// due to the lack of `.git` suffix or because its basename is not `.git` as in `worktree/.git`.
+ pub fn open_path_as_is(mut self, enable: bool) -> Self {
+ self.open_path_as_is = enable;
+ self
+ }
+
+ /// Set the trust level of the `.git` directory we are about to open.
+ ///
+ /// This can be set manually to force trust even though otherwise it might
+ /// not be fully trusted, leading to limitations in how configuration files
+ /// are interpreted.
+ ///
+ /// If not called explicitly, it will be determined by looking at its
+ /// ownership via [`gix_sec::Trust::from_path_ownership()`].
+ ///
+ /// # Security Warning
+ ///
+ /// Use with extreme care and only if it's absolutely known that the repository
+ /// is always controlled by the desired user. Using this capability _only_ saves
+ /// a permission check and only so if the [`open()`][Self::open()] method is used,
+ /// as opposed to discovery.
+ pub fn with(mut self, trust: gix_sec::Trust) -> Self {
+ self.git_dir_trust = trust.into();
+ self
+ }
+
+ /// If true, default false, and if the repository's trust level is not `Full`
+ /// (see [`with()`][Self::with()] for more), then the open operation will fail.
+ ///
+ /// Use this to mimic `git`s way of handling untrusted repositories. Note that `gitoxide` solves
+ /// this by not using configuration from untrusted sources and by generally being secured against
+ /// doctored input files which at worst could cause out-of-memory at the time of writing.
+ pub fn bail_if_untrusted(mut self, toggle: bool) -> Self {
+ self.bail_if_untrusted = toggle;
+ self
+ }
+
+ /// Set the filter which determines if a configuration section can be used to read values from,
+ /// hence it returns true if it is eligible.
+ ///
+ /// The default filter selects sections whose trust level is [`full`][gix_sec::Trust::Full] or
+ /// whose source is not [`repository-local`][gix_config::source::Kind::Repository].
+ pub fn filter_config_section(mut self, filter: fn(&gix_config::file::Metadata) -> bool) -> Self {
+ self.filter_config_section = Some(filter);
+ self
+ }
+
+ /// By default, in release mode configuration will be read without retaining non-essential information like
+ /// comments or whitespace to optimize lookup performance.
+ ///
+ /// Some application might want to toggle this to false in they want to display or edit configuration losslessly
+ /// with all whitespace and comments included.
+ pub fn lossy_config(mut self, toggle: bool) -> Self {
+ self.lossy_config = toggle.into();
+ self
+ }
+
+ /// If set, default is false, invalid configuration values will cause an error even if these can safely be defaulted.
+ ///
+ /// This is recommended for all applications that prefer correctness over usability.
+ /// `git` itself defaults to strict configuration mode, flagging incorrect configuration immediately.
+ pub fn strict_config(mut self, toggle: bool) -> Self {
+ self.lenient_config = !toggle;
+ self
+ }
+
+ /// Open a repository at `path` with the options set so far.
+ #[allow(clippy::result_large_err)]
+ pub fn open(self, path: impl Into<PathBuf>) -> Result<ThreadSafeRepository, Error> {
+ ThreadSafeRepository::open_opts(path, self)
+ }
+}
+
+impl gix_sec::trust::DefaultForLevel for Options {
+ fn default_for_level(level: gix_sec::Trust) -> Self {
+ match level {
+ gix_sec::Trust::Full => Options {
+ object_store_slots: Default::default(),
+ permissions: Permissions::default_for_level(level),
+ git_dir_trust: gix_sec::Trust::Full.into(),
+ filter_config_section: Some(config::section::is_trusted),
+ lossy_config: None,
+ bail_if_untrusted: false,
+ lenient_config: true,
+ open_path_as_is: false,
+ api_config_overrides: Vec::new(),
+ cli_config_overrides: Vec::new(),
+ current_dir: None,
+ },
+ gix_sec::Trust::Reduced => Options {
+ object_store_slots: gix_odb::store::init::Slots::Given(32), // limit resource usage
+ permissions: Permissions::default_for_level(level),
+ git_dir_trust: gix_sec::Trust::Reduced.into(),
+ filter_config_section: Some(config::section::is_trusted),
+ bail_if_untrusted: false,
+ lenient_config: true,
+ open_path_as_is: false,
+ lossy_config: None,
+ api_config_overrides: Vec::new(),
+ cli_config_overrides: Vec::new(),
+ current_dir: None,
+ },
+ }
+ }
+}
diff --git a/vendor/gix/src/open/repository.rs b/vendor/gix/src/open/repository.rs
new file mode 100644
index 000000000..85dd91da7
--- /dev/null
+++ b/vendor/gix/src/open/repository.rs
@@ -0,0 +1,345 @@
+#![allow(clippy::result_large_err)]
+use std::{borrow::Cow, path::PathBuf};
+
+use gix_features::threading::OwnShared;
+
+use super::{Error, Options};
+use crate::{
+ config,
+ config::{
+ cache::{interpolate_context, util::ApplyLeniency},
+ tree::{gitoxide, Core, Key, Safe},
+ },
+ permission, Permissions, ThreadSafeRepository,
+};
+
+#[derive(Default, Clone)]
+pub(crate) struct EnvironmentOverrides {
+ /// An override of the worktree typically from the environment, and overrides even worktree dirs set as parameter.
+ ///
+ /// This emulates the way git handles this override.
+ worktree_dir: Option<PathBuf>,
+ /// An override for the .git directory, typically from the environment.
+ ///
+ /// If set, the passed in `git_dir` parameter will be ignored in favor of this one.
+ git_dir: Option<PathBuf>,
+}
+
+impl EnvironmentOverrides {
+ fn from_env() -> Result<Self, permission::env_var::resource::Error> {
+ let mut worktree_dir = None;
+ if let Some(path) = std::env::var_os(Core::WORKTREE.the_environment_override()) {
+ worktree_dir = PathBuf::from(path).into();
+ }
+ let mut git_dir = None;
+ if let Some(path) = std::env::var_os("GIT_DIR") {
+ git_dir = PathBuf::from(path).into();
+ }
+ Ok(EnvironmentOverrides { worktree_dir, git_dir })
+ }
+}
+
+impl ThreadSafeRepository {
+ /// Open a git repository at the given `path`, possibly expanding it to `path/.git` if `path` is a work tree dir.
+ pub fn open(path: impl Into<PathBuf>) -> Result<Self, Error> {
+ Self::open_opts(path, Options::default())
+ }
+
+ /// Open a git repository at the given `path`, possibly expanding it to `path/.git` if `path` is a work tree dir, and use
+ /// `options` for fine-grained control.
+ ///
+ /// Note that you should use [`crate::discover()`] if security should be adjusted by ownership.
+ pub fn open_opts(path: impl Into<PathBuf>, mut options: Options) -> Result<Self, Error> {
+ let (path, kind) = {
+ let path = path.into();
+ let looks_like_git_dir =
+ path.ends_with(gix_discover::DOT_GIT_DIR) || path.extension() == Some(std::ffi::OsStr::new("git"));
+ let candidate = if !options.open_path_as_is && !looks_like_git_dir {
+ Cow::Owned(path.join(gix_discover::DOT_GIT_DIR))
+ } else {
+ Cow::Borrowed(&path)
+ };
+ match gix_discover::is_git(candidate.as_ref()) {
+ Ok(kind) => (candidate.into_owned(), kind),
+ Err(err) => {
+ if options.open_path_as_is || matches!(candidate, Cow::Borrowed(_)) {
+ return Err(Error::NotARepository {
+ source: err,
+ path: candidate.into_owned(),
+ });
+ }
+ match gix_discover::is_git(&path) {
+ Ok(kind) => (path, kind),
+ Err(err) => return Err(Error::NotARepository { source: err, path }),
+ }
+ }
+ }
+ };
+ let cwd = std::env::current_dir()?;
+ let (git_dir, worktree_dir) = gix_discover::repository::Path::from_dot_git_dir(path, kind, &cwd)
+ .expect("we have sanitized path with is_git()")
+ .into_repository_and_work_tree_directories();
+ if options.git_dir_trust.is_none() {
+ options.git_dir_trust = gix_sec::Trust::from_path_ownership(&git_dir)?.into();
+ }
+ options.current_dir = Some(cwd);
+ ThreadSafeRepository::open_from_paths(git_dir, worktree_dir, options)
+ }
+
+ /// Try to open a git repository in `fallback_directory` (can be worktree or `.git` directory) only if there is no override
+ /// from of the `gitdir` using git environment variables.
+ ///
+ /// Use the `trust_map` to apply options depending in the trust level for `directory` or the directory it's overridden with.
+ /// The `.git` directory whether given or computed is used for trust checks.
+ ///
+ /// Note that this will read various `GIT_*` environment variables to check for overrides, and is probably most useful when implementing
+ /// custom hooks.
+ // TODO: tests, with hooks, GIT_QUARANTINE for ref-log and transaction control (needs gix-sec support to remove write access in gix-ref)
+ // TODO: The following vars should end up as overrides of the respective configuration values (see gix-config).
+ // GIT_PROXY_SSL_CERT, GIT_PROXY_SSL_KEY, GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED.
+ // GIT_PROXY_SSL_CAINFO, GIT_SSL_CIPHER_LIST, GIT_HTTP_MAX_REQUESTS, GIT_CURL_FTP_NO_EPSV,
+ pub fn open_with_environment_overrides(
+ fallback_directory: impl Into<PathBuf>,
+ trust_map: gix_sec::trust::Mapping<Options>,
+ ) -> Result<Self, Error> {
+ let overrides = EnvironmentOverrides::from_env()?;
+ let (path, path_kind): (PathBuf, _) = match overrides.git_dir {
+ Some(git_dir) => gix_discover::is_git(&git_dir)
+ .map_err(|err| Error::NotARepository {
+ source: err,
+ path: git_dir.clone(),
+ })
+ .map(|kind| (git_dir, kind))?,
+ None => {
+ let fallback_directory = fallback_directory.into();
+ gix_discover::is_git(&fallback_directory)
+ .map_err(|err| Error::NotARepository {
+ source: err,
+ path: fallback_directory.clone(),
+ })
+ .map(|kind| (fallback_directory, kind))?
+ }
+ };
+
+ let cwd = std::env::current_dir()?;
+ let (git_dir, worktree_dir) = gix_discover::repository::Path::from_dot_git_dir(path, path_kind, &cwd)
+ .expect("we have sanitized path with is_git()")
+ .into_repository_and_work_tree_directories();
+ let worktree_dir = worktree_dir.or(overrides.worktree_dir);
+
+ let git_dir_trust = gix_sec::Trust::from_path_ownership(&git_dir)?;
+ let mut options = trust_map.into_value_by_level(git_dir_trust);
+ options.current_dir = Some(cwd);
+ ThreadSafeRepository::open_from_paths(git_dir, worktree_dir, options)
+ }
+
+ pub(crate) fn open_from_paths(
+ git_dir: PathBuf,
+ mut worktree_dir: Option<PathBuf>,
+ options: Options,
+ ) -> Result<Self, Error> {
+ let Options {
+ git_dir_trust,
+ object_store_slots,
+ filter_config_section,
+ lossy_config,
+ lenient_config,
+ bail_if_untrusted,
+ open_path_as_is: _,
+ permissions: Permissions { ref env, config },
+ ref api_config_overrides,
+ ref cli_config_overrides,
+ ref current_dir,
+ } = options;
+ let current_dir = current_dir.as_deref().expect("BUG: current_dir must be set by caller");
+ let git_dir_trust = git_dir_trust.expect("trust must be been determined by now");
+
+ // TODO: assure we handle the worktree-dir properly as we can have config per worktree with an extension.
+ // This would be something read in later as have to first check for extensions. Also this means
+ // that each worktree, even if accessible through this instance, has to come in its own Repository instance
+ // as it may have its own configuration. That's fine actually.
+ let common_dir = gix_discover::path::from_plain_file(git_dir.join("commondir"))
+ .transpose()?
+ .map(|cd| git_dir.join(cd));
+ let common_dir_ref = common_dir.as_deref().unwrap_or(&git_dir);
+
+ let repo_config = config::cache::StageOne::new(
+ common_dir_ref,
+ git_dir.as_ref(),
+ git_dir_trust,
+ lossy_config,
+ lenient_config,
+ )?;
+ let mut refs = {
+ let reflog = repo_config.reflog.unwrap_or(gix_ref::store::WriteReflog::Disable);
+ let object_hash = repo_config.object_hash;
+ match &common_dir {
+ Some(common_dir) => crate::RefStore::for_linked_worktree(&git_dir, common_dir, reflog, object_hash),
+ None => crate::RefStore::at(&git_dir, reflog, object_hash),
+ }
+ };
+ let head = refs.find("HEAD").ok();
+ let git_install_dir = crate::path::install_dir().ok();
+ let home = std::env::var_os("HOME")
+ .map(PathBuf::from)
+ .and_then(|home| env.home.check_opt(home));
+
+ let mut filter_config_section = filter_config_section.unwrap_or(config::section::is_trusted);
+ let config = config::Cache::from_stage_one(
+ repo_config,
+ common_dir_ref,
+ head.as_ref().and_then(|head| head.target.try_name()),
+ filter_config_section,
+ git_install_dir.as_deref(),
+ home.as_deref(),
+ env.clone(),
+ config,
+ lenient_config,
+ api_config_overrides,
+ cli_config_overrides,
+ )?;
+
+ if bail_if_untrusted && git_dir_trust != gix_sec::Trust::Full {
+ check_safe_directories(&git_dir, git_install_dir.as_deref(), home.as_deref(), &config)?;
+ }
+
+ // core.worktree might be used to overwrite the worktree directory
+ if !config.is_bare {
+ if let Some(wt) = config
+ .resolved
+ .path_filter("core", None, Core::WORKTREE.name, &mut filter_config_section)
+ {
+ let wt_path = wt
+ .interpolate(interpolate_context(git_install_dir.as_deref(), home.as_deref()))
+ .map_err(config::Error::PathInterpolation)?;
+ worktree_dir = {
+ gix_path::normalize(git_dir.join(wt_path), current_dir)
+ .and_then(|wt| wt.as_ref().is_dir().then(|| wt.into_owned()))
+ }
+ }
+ }
+
+ match worktree_dir {
+ None if !config.is_bare => {
+ worktree_dir = Some(git_dir.parent().expect("parent is always available").to_owned());
+ }
+ Some(_) => {
+ // note that we might be bare even with a worktree directory - work trees don't have to be
+ // the parent of a non-bare repository.
+ }
+ None => {}
+ }
+
+ refs.write_reflog = config::cache::util::reflog_or_default(config.reflog, worktree_dir.is_some());
+ let replacements = replacement_objects_refs_prefix(&config.resolved, lenient_config, filter_config_section)?
+ .and_then(|prefix| {
+ let platform = refs.iter().ok()?;
+ let iter = platform.prefixed(&prefix).ok()?;
+ let prefix = prefix.to_str()?;
+ let replacements = iter
+ .filter_map(Result::ok)
+ .filter_map(|r: gix_ref::Reference| {
+ let target = r.target.try_id()?.to_owned();
+ let source =
+ gix_hash::ObjectId::from_hex(r.name.as_bstr().strip_prefix(prefix.as_bytes())?).ok()?;
+ Some((source, target))
+ })
+ .collect::<Vec<_>>();
+ Some(replacements)
+ })
+ .unwrap_or_default();
+
+ Ok(ThreadSafeRepository {
+ objects: OwnShared::new(gix_odb::Store::at_opts(
+ common_dir_ref.join("objects"),
+ replacements,
+ gix_odb::store::init::Options {
+ slots: object_store_slots,
+ object_hash: config.object_hash,
+ use_multi_pack_index: config.use_multi_pack_index,
+ current_dir: current_dir.to_owned().into(),
+ },
+ )?),
+ common_dir,
+ refs,
+ work_tree: worktree_dir,
+ config,
+ // used when spawning new repositories off this one when following worktrees
+ linked_worktree_options: options,
+ index: gix_features::fs::MutableSnapshot::new().into(),
+ })
+ }
+}
+
+// TODO: tests
+fn replacement_objects_refs_prefix(
+ config: &gix_config::File<'static>,
+ lenient: bool,
+ mut filter_config_section: fn(&gix_config::file::Metadata) -> bool,
+) -> Result<Option<PathBuf>, Error> {
+ let is_disabled = config
+ .boolean_filter_by_key("gitoxide.objects.noReplace", &mut filter_config_section)
+ .map(|b| gitoxide::Objects::NO_REPLACE.enrich_error(b))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::Error::ConfigBoolean)?
+ .unwrap_or_default();
+
+ if is_disabled {
+ return Ok(None);
+ }
+
+ let ref_base = gix_path::from_bstr({
+ let key = "gitoxide.objects.replaceRefBase";
+ debug_assert_eq!(gitoxide::Objects::REPLACE_REF_BASE.logical_name(), key);
+ config
+ .string_filter_by_key(key, &mut filter_config_section)
+ .unwrap_or_else(|| Cow::Borrowed("refs/replace/".into()))
+ })
+ .into_owned();
+ Ok(ref_base.into())
+}
+
+fn check_safe_directories(
+ git_dir: &std::path::Path,
+ git_install_dir: Option<&std::path::Path>,
+ home: Option<&std::path::Path>,
+ config: &config::Cache,
+) -> Result<(), Error> {
+ let mut is_safe = false;
+ let git_dir = match gix_path::realpath(git_dir) {
+ Ok(p) => p,
+ Err(_) => git_dir.to_owned(),
+ };
+ for safe_dir in config
+ .resolved
+ .strings_filter("safe", None, Safe::DIRECTORY.name, &mut Safe::directory_filter)
+ .unwrap_or_default()
+ {
+ if safe_dir.as_ref() == "*" {
+ is_safe = true;
+ continue;
+ }
+ if safe_dir.is_empty() {
+ is_safe = false;
+ continue;
+ }
+ if !is_safe {
+ let safe_dir = match gix_config::Path::from(std::borrow::Cow::Borrowed(safe_dir.as_ref()))
+ .interpolate(interpolate_context(git_install_dir, home))
+ {
+ Ok(path) => path,
+ Err(_) => gix_path::from_bstr(safe_dir),
+ };
+ if safe_dir == git_dir {
+ is_safe = true;
+ continue;
+ }
+ }
+ }
+ if is_safe {
+ Ok(())
+ } else {
+ Err(Error::UnsafeGitDir { path: git_dir })
+ }
+}