Mercurial > public > mercurial-scm > hg-stable
diff rust/hg-core/src/repo.rs @ 47779:cf5f8da2244c stable
rhg: Propagate permission errors when finding a repository
The Rust standard library has a `Path::is_dir` method that returns false
for any I/O error (such as a permission error),
not just "No such file or directory".
Instead add an `is_dir` function that returns false for non-directories
and for "No such file or directory" errors, but propagates other I/O errors.
Differential Revision: https://phab.mercurial-scm.org/D11230
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 29 Jul 2021 12:22:25 +0200 |
parents | ff97e793ed36 |
children | 696abab107b4 |
line wrap: on
line diff
--- a/rust/hg-core/src/repo.rs Thu Jul 29 11:53:03 2021 +0200 +++ b/rust/hg-core/src/repo.rs Thu Jul 29 12:22:25 2021 +0200 @@ -6,6 +6,7 @@ use crate::utils::SliceExt; use memmap::{Mmap, MmapOptions}; use std::collections::HashSet; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; /// A repository on disk @@ -51,7 +52,7 @@ // ancestors() is inclusive: it first yields `current_directory` // as-is. for ancestor in current_directory.ancestors() { - if ancestor.join(".hg").is_dir() { + if is_dir(ancestor.join(".hg"))? { return Ok(ancestor.to_path_buf()); } } @@ -73,9 +74,9 @@ explicit_path: Option<PathBuf>, ) -> Result<Self, RepoError> { if let Some(root) = explicit_path { - if root.join(".hg").is_dir() { + if is_dir(root.join(".hg"))? { Self::new_at_path(root.to_owned(), config) - } else if root.is_file() { + } else if is_file(&root)? { Err(HgError::unsupported("bundle repository").into()) } else { Err(RepoError::NotFound { @@ -130,7 +131,7 @@ if relative { shared_path = dot_hg.join(shared_path) } - if !shared_path.is_dir() { + if !is_dir(&shared_path)? { return Err(HgError::corrupted(format!( ".hg/sharedpath points to nonexistent directory {}", shared_path.display() @@ -286,3 +287,29 @@ .with_context(|| IoErrorContext::RenamingFile { from, to }) } } + +fn fs_metadata( + path: impl AsRef<Path>, +) -> Result<Option<std::fs::Metadata>, HgError> { + let path = path.as_ref(); + match std::fs::metadata(path) { + Ok(meta) => Ok(Some(meta)), + Err(error) => match error.kind() { + // TODO: when we require a Rust version where `NotADirectory` is + // stable, invert this logic and return None for it and `NotFound` + // and propagate any other error. + ErrorKind::PermissionDenied => Err(error).with_context(|| { + IoErrorContext::ReadingMetadata(path.to_owned()) + }), + _ => Ok(None), + }, + } +} + +fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> { + Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir())) +} + +fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> { + Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file())) +}