Mercurial > public > mercurial-scm > hg-stable
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 } |