Skip to content

Commit 6d77799

Browse files
authored
Merge pull request #1917 from Xavrir/issue-1900
fix: handle invalid working directories gracefully with --full-path
2 parents db7d448 + 807aa11 commit 6d77799

5 files changed

Lines changed: 85 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Unreleased
2+
3+
## Bugfixes
4+
- Handle invalid working directories gracefully when using `--full-path`, see #1900 (@Xavrir).
5+
16
# 10.4.2
27

38
## Bugfixes

src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ pub struct Config {
1515
/// Whether the search is case-sensitive or case-insensitive.
1616
pub case_sensitive: bool,
1717

18-
/// Whether to search within the full file path or just the base name (filename or directory
19-
/// name).
20-
pub search_full_path: bool,
18+
/// Cached current working directory for absolute path construction.
19+
/// Populated when `--full-path` is set; `None` means search by filename only.
20+
pub cwd: Option<PathBuf>,
2121

2222
/// Whether to ignore hidden files and directories (or not).
2323
pub ignore_hidden: bool,

src/filesystem.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ pub fn path_absolute_form(path: &Path) -> io::Result<PathBuf> {
2020
env::current_dir().map(|path_buf| path_buf.join(path))
2121
}
2222

23+
/// Construct an absolute path from a potentially relative path and a
24+
/// pre-resolved working directory. Unlike `path_absolute_form`, this
25+
/// does not call `env::current_dir()` and cannot fail.
26+
pub fn make_absolute(path: &Path, cwd: &Path) -> PathBuf {
27+
if path.is_absolute() {
28+
return path.to_path_buf();
29+
}
30+
let path = path.strip_prefix(".").unwrap_or(path);
31+
cwd.join(path)
32+
}
33+
2334
pub fn absolute_path(path: &Path) -> io::Result<PathBuf> {
2435
let path_buf = path_absolute_form(path)?;
2536

@@ -153,4 +164,40 @@ mod tests {
153164
Path::new("foo/bar/baz")
154165
);
155166
}
167+
168+
#[test]
169+
fn make_absolute_with_relative_path() {
170+
use super::make_absolute;
171+
use std::path::PathBuf;
172+
173+
let cwd = Path::new("/home/user");
174+
assert_eq!(
175+
make_absolute(Path::new("foo/bar"), cwd),
176+
PathBuf::from("/home/user/foo/bar")
177+
);
178+
}
179+
180+
#[test]
181+
fn make_absolute_strips_dot_prefix() {
182+
use super::make_absolute;
183+
use std::path::PathBuf;
184+
185+
let cwd = Path::new("/home/user");
186+
assert_eq!(
187+
make_absolute(Path::new("./foo/bar"), cwd),
188+
PathBuf::from("/home/user/foo/bar")
189+
);
190+
}
191+
192+
#[test]
193+
fn make_absolute_with_absolute_path() {
194+
use super::make_absolute;
195+
use std::path::PathBuf;
196+
197+
let cwd = Path::new("/home/user");
198+
assert_eq!(
199+
make_absolute(Path::new("/absolute/path"), cwd),
200+
PathBuf::from("/absolute/path")
201+
);
202+
}
156203
}

src/main.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,18 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
245245
let command = extract_command(&mut opts, colored_output)?;
246246
let has_command = command.is_some();
247247

248+
let cwd = if opts.full_path {
249+
Some(env::current_dir().context(
250+
"Could not determine current directory. \
251+
This is required for --full-path.",
252+
)?)
253+
} else {
254+
None
255+
};
256+
248257
Ok(Config {
249258
case_sensitive,
250-
search_full_path: opts.full_path,
259+
cwd,
251260
ignore_hidden: !(opts.hidden || opts.rg_alias_ignore()),
252261
read_fdignore: !(opts.no_ignore || opts.rg_alias_ignore()),
253262
read_vcsignore: !(opts.no_ignore || opts.rg_alias_ignore() || opts.no_ignore_vcs),

src/walk.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -524,20 +524,7 @@ impl WorkerState {
524524
// Check the name first, since it doesn't require metadata
525525
let entry_path = entry.path();
526526

527-
let search_str: Cow<OsStr> = if config.search_full_path {
528-
let path_abs_buf = filesystem::path_absolute_form(entry_path)
529-
.expect("Retrieving absolute path succeeds");
530-
Cow::Owned(path_abs_buf.as_os_str().to_os_string())
531-
} else {
532-
match entry_path.file_name() {
533-
Some(filename) => Cow::Borrowed(filename),
534-
None => unreachable!(
535-
"Encountered file system entry without a file name. This should only \
536-
happen for paths like 'foo/bar/..' or '/' which are not supposed to \
537-
appear in a file system traversal."
538-
),
539-
}
540-
};
527+
let search_str = search_str_for_entry(entry_path, config.cwd.as_deref());
541528

542529
if !patterns
543530
.iter()
@@ -676,6 +663,25 @@ impl WorkerState {
676663
}
677664
}
678665

666+
fn search_str_for_entry<'a>(
667+
entry_path: &'a std::path::Path,
668+
cwd: Option<&std::path::Path>,
669+
) -> Cow<'a, OsStr> {
670+
if let Some(cwd) = cwd {
671+
let abs_path = filesystem::make_absolute(entry_path, cwd);
672+
Cow::Owned(abs_path.into_os_string())
673+
} else {
674+
match entry_path.file_name() {
675+
Some(filename) => Cow::Borrowed(filename),
676+
None => unreachable!(
677+
"Encountered file system entry without a file name. This should only \
678+
happen for paths like 'foo/bar/..' or '/' which are not supposed to \
679+
appear in a file system traversal."
680+
),
681+
}
682+
}
683+
}
684+
679685
/// Recursively scan the given search path for files / pathnames matching the patterns.
680686
///
681687
/// If the `--exec` argument was supplied, this will create a thread pool for executing

0 commit comments

Comments
 (0)