comparison 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
comparison
equal deleted inserted replaced
47778:6df528ed47a9 47779:cf5f8da2244c
4 use crate::requirements; 4 use crate::requirements;
5 use crate::utils::files::get_path_from_bytes; 5 use crate::utils::files::get_path_from_bytes;
6 use crate::utils::SliceExt; 6 use crate::utils::SliceExt;
7 use memmap::{Mmap, MmapOptions}; 7 use memmap::{Mmap, MmapOptions};
8 use std::collections::HashSet; 8 use std::collections::HashSet;
9 use std::io::ErrorKind;
9 use std::path::{Path, PathBuf}; 10 use std::path::{Path, PathBuf};
10 11
11 /// A repository on disk 12 /// A repository on disk
12 pub struct Repo { 13 pub struct Repo {
13 working_directory: PathBuf, 14 working_directory: PathBuf,
49 pub fn find_repo_root() -> Result<PathBuf, RepoError> { 50 pub fn find_repo_root() -> Result<PathBuf, RepoError> {
50 let current_directory = crate::utils::current_dir()?; 51 let current_directory = crate::utils::current_dir()?;
51 // ancestors() is inclusive: it first yields `current_directory` 52 // ancestors() is inclusive: it first yields `current_directory`
52 // as-is. 53 // as-is.
53 for ancestor in current_directory.ancestors() { 54 for ancestor in current_directory.ancestors() {
54 if ancestor.join(".hg").is_dir() { 55 if is_dir(ancestor.join(".hg"))? {
55 return Ok(ancestor.to_path_buf()); 56 return Ok(ancestor.to_path_buf());
56 } 57 }
57 } 58 }
58 return Err(RepoError::NotFound { 59 return Err(RepoError::NotFound {
59 at: current_directory, 60 at: current_directory,
71 pub fn find( 72 pub fn find(
72 config: &Config, 73 config: &Config,
73 explicit_path: Option<PathBuf>, 74 explicit_path: Option<PathBuf>,
74 ) -> Result<Self, RepoError> { 75 ) -> Result<Self, RepoError> {
75 if let Some(root) = explicit_path { 76 if let Some(root) = explicit_path {
76 if root.join(".hg").is_dir() { 77 if is_dir(root.join(".hg"))? {
77 Self::new_at_path(root.to_owned(), config) 78 Self::new_at_path(root.to_owned(), config)
78 } else if root.is_file() { 79 } else if is_file(&root)? {
79 Err(HgError::unsupported("bundle repository").into()) 80 Err(HgError::unsupported("bundle repository").into())
80 } else { 81 } else {
81 Err(RepoError::NotFound { 82 Err(RepoError::NotFound {
82 at: root.to_owned(), 83 at: root.to_owned(),
83 }) 84 })
128 let mut shared_path = 129 let mut shared_path =
129 get_path_from_bytes(bytes.trim_end_newlines()).to_owned(); 130 get_path_from_bytes(bytes.trim_end_newlines()).to_owned();
130 if relative { 131 if relative {
131 shared_path = dot_hg.join(shared_path) 132 shared_path = dot_hg.join(shared_path)
132 } 133 }
133 if !shared_path.is_dir() { 134 if !is_dir(&shared_path)? {
134 return Err(HgError::corrupted(format!( 135 return Err(HgError::corrupted(format!(
135 ".hg/sharedpath points to nonexistent directory {}", 136 ".hg/sharedpath points to nonexistent directory {}",
136 shared_path.display() 137 shared_path.display()
137 )) 138 ))
138 .into()); 139 .into());
284 let to = self.join(relative_to); 285 let to = self.join(relative_to);
285 std::fs::rename(&from, &to) 286 std::fs::rename(&from, &to)
286 .with_context(|| IoErrorContext::RenamingFile { from, to }) 287 .with_context(|| IoErrorContext::RenamingFile { from, to })
287 } 288 }
288 } 289 }
290
291 fn fs_metadata(
292 path: impl AsRef<Path>,
293 ) -> Result<Option<std::fs::Metadata>, HgError> {
294 let path = path.as_ref();
295 match std::fs::metadata(path) {
296 Ok(meta) => Ok(Some(meta)),
297 Err(error) => match error.kind() {
298 // TODO: when we require a Rust version where `NotADirectory` is
299 // stable, invert this logic and return None for it and `NotFound`
300 // and propagate any other error.
301 ErrorKind::PermissionDenied => Err(error).with_context(|| {
302 IoErrorContext::ReadingMetadata(path.to_owned())
303 }),
304 _ => Ok(None),
305 },
306 }
307 }
308
309 fn is_dir(path: impl AsRef<Path>) -> Result<bool, HgError> {
310 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_dir()))
311 }
312
313 fn is_file(path: impl AsRef<Path>) -> Result<bool, HgError> {
314 Ok(fs_metadata(path)?.map_or(false, |meta| meta.is_file()))
315 }