cap_primitives/fs/
canonicalize.rs1use crate::fs::canonicalize_impl;
4#[cfg(racy_asserts)]
5use crate::fs::{file_path, open, OpenOptions};
6use std::path::{Path, PathBuf};
7use std::{fs, io};
8
9#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
12#[inline]
13pub fn canonicalize(start: &fs::File, path: &Path) -> io::Result<PathBuf> {
14 let result = canonicalize_impl(start, path);
16
17 #[cfg(racy_asserts)]
18 check_canonicalize(start, path, &result);
19
20 result
21}
22
23#[cfg(racy_asserts)]
24fn check_canonicalize(start: &fs::File, path: &Path, result: &io::Result<PathBuf>) {
25 if let Ok(canonical_path) = result {
26 let path_result = open(start, path, OpenOptions::new().read(true));
27 let canonical_result = open(start, canonical_path, OpenOptions::new().read(true));
28 match (path_result, canonical_result) {
29 (Ok(path_file), Ok(canonical_file)) => assert_same_file!(
30 &path_file,
31 &canonical_file,
32 "we should be able to stat paths that we just canonicalized"
33 ),
34 (Err(path_err), Err(canonical_err)) => {
35 assert_eq!(path_err.to_string(), canonical_err.to_string())
36 }
37 other => {
38 if !path.to_string_lossy().ends_with("/.") {
40 panic!("inconsistent canonicalize checks: {:?}", other);
41 }
42 }
43 }
44
45 if let Some(start_abspath) = file_path(start) {
49 let check_abspath = start_abspath.join(path);
50 let result_abspath = start_abspath.join(canonical_path);
51 if let Ok(check_abspath) = fs::canonicalize(check_abspath) {
52 let result_abspath =
53 fs::canonicalize(result_abspath).expect("we already canonicalized this");
54 assert_eq!(
55 check_abspath,
56 result_abspath,
57 "incorrect canonicalization: start='{:?}' path='{}' result='{}'",
58 start,
59 path.display(),
60 canonical_path.display()
61 );
62 assert!(
65 result_abspath.starts_with(start_abspath),
66 "sandbox escape: start='{:?}' path='{}' result='{}'",
67 start,
68 path.display(),
69 canonical_path.display()
70 );
71 }
72 }
73 }
74}