/rust/registry/src/index.crates.io-6f17d22bba15001f/cap-primitives-3.4.4/src/fs/open.rs
Line | Count | Source |
1 | | //! This defines `open`, the primary entrypoint to sandboxed file and directory |
2 | | //! opening. |
3 | | |
4 | | #[cfg(racy_asserts)] |
5 | | use crate::fs::{file_path, open_unchecked, stat_unchecked, Metadata}; |
6 | | use crate::fs::{open_impl, OpenOptions}; |
7 | | use std::path::Path; |
8 | | use std::{fs, io}; |
9 | | |
10 | | /// Perform an `openat`-like operation, ensuring that the resolution of the |
11 | | /// path never escapes the directory tree rooted at `start`. |
12 | | #[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))] |
13 | | #[inline] |
14 | 432k | pub fn open(start: &fs::File, path: &Path, options: &OpenOptions) -> io::Result<fs::File> { |
15 | 432k | #[cfg(racy_asserts)] |
16 | 432k | let stat_before = stat_unchecked(start, path, options.follow); |
17 | 432k | |
18 | 432k | // Call the underlying implementation. |
19 | 432k | let result = open_impl(start, path, options); |
20 | 432k | |
21 | 432k | #[cfg(racy_asserts)] |
22 | 432k | let stat_after = stat_unchecked(start, path, options.follow); |
23 | 432k | |
24 | 432k | #[cfg(racy_asserts)] |
25 | 432k | check_open(start, path, options, &stat_before, &result, &stat_after); |
26 | 432k | |
27 | 432k | result |
28 | 432k | } cap_primitives::fs::open::open Line | Count | Source | 14 | 1.75k | pub fn open(start: &fs::File, path: &Path, options: &OpenOptions) -> io::Result<fs::File> { | 15 | 1.75k | #[cfg(racy_asserts)] | 16 | 1.75k | let stat_before = stat_unchecked(start, path, options.follow); | 17 | 1.75k | | 18 | 1.75k | // Call the underlying implementation. | 19 | 1.75k | let result = open_impl(start, path, options); | 20 | 1.75k | | 21 | 1.75k | #[cfg(racy_asserts)] | 22 | 1.75k | let stat_after = stat_unchecked(start, path, options.follow); | 23 | 1.75k | | 24 | 1.75k | #[cfg(racy_asserts)] | 25 | 1.75k | check_open(start, path, options, &stat_before, &result, &stat_after); | 26 | 1.75k | | 27 | 1.75k | result | 28 | 1.75k | } |
cap_primitives::fs::open::open Line | Count | Source | 14 | 430k | pub fn open(start: &fs::File, path: &Path, options: &OpenOptions) -> io::Result<fs::File> { | 15 | 430k | #[cfg(racy_asserts)] | 16 | 430k | let stat_before = stat_unchecked(start, path, options.follow); | 17 | 430k | | 18 | 430k | // Call the underlying implementation. | 19 | 430k | let result = open_impl(start, path, options); | 20 | 430k | | 21 | 430k | #[cfg(racy_asserts)] | 22 | 430k | let stat_after = stat_unchecked(start, path, options.follow); | 23 | 430k | | 24 | 430k | #[cfg(racy_asserts)] | 25 | 430k | check_open(start, path, options, &stat_before, &result, &stat_after); | 26 | 430k | | 27 | 430k | result | 28 | 430k | } |
|
29 | | |
30 | | #[cfg(racy_asserts)] |
31 | | fn check_open( |
32 | | start: &fs::File, |
33 | | path: &Path, |
34 | | options: &OpenOptions, |
35 | | _stat_before: &io::Result<Metadata>, |
36 | | result: &io::Result<fs::File>, |
37 | | _stat_after: &io::Result<Metadata>, |
38 | | ) { |
39 | | let unchecked_result = open_unchecked( |
40 | | start, |
41 | | path, |
42 | | options |
43 | | .clone() |
44 | | .create(false) |
45 | | .create_new(false) |
46 | | .truncate(false), |
47 | | ); |
48 | | |
49 | | match (&result, &unchecked_result) { |
50 | | (Ok(result_file), Ok(unchecked_file)) => { |
51 | | assert_same_file!( |
52 | | &result_file, |
53 | | &unchecked_file, |
54 | | "path resolution inconsistency: start='{:?}', path='{}'", |
55 | | start, |
56 | | path.display(), |
57 | | ); |
58 | | } |
59 | | (Ok(result_file), Err(unchecked_error)) => { |
60 | | if unchecked_error.kind() == io::ErrorKind::PermissionDenied { |
61 | | assert!(options.create || options.create_new); |
62 | | } else { |
63 | | panic!( |
64 | | "unexpected success opening start='{:?}', path='{}'; expected {:?}; got {:?}", |
65 | | start, |
66 | | path.display(), |
67 | | unchecked_error, |
68 | | result_file |
69 | | ); |
70 | | } |
71 | | } |
72 | | (Err(result_error), Ok(_unchecked_file)) => match result_error.kind() { |
73 | | io::ErrorKind::PermissionDenied | io::ErrorKind::InvalidInput => (), |
74 | | io::ErrorKind::AlreadyExists if options.create_new => (), |
75 | | _ => panic!( |
76 | | "unexpected error opening start='{:?}', path='{}': {:?}", |
77 | | start, |
78 | | path.display(), |
79 | | result_error |
80 | | ), |
81 | | }, |
82 | | (Err(result_error), Err(_unchecked_error)) => match result_error.kind() { |
83 | | io::ErrorKind::PermissionDenied | io::ErrorKind::InvalidInput => (), |
84 | | _ => { |
85 | | /* TODO: Check error messages. |
86 | | let unchecked_error = unchecked_error.into(); |
87 | | assert_eq!(result_error.to_string(), unchecked_error.to_string()); |
88 | | assert_eq!(result_error.kind(), unchecked_error.kind()); |
89 | | */ |
90 | | } |
91 | | }, |
92 | | } |
93 | | |
94 | | // On operating systems which can tell us the path of a file descriptor, |
95 | | // assert that the start path is a parent of the result path. |
96 | | if let Ok(result_file) = &result { |
97 | | if let Some(result_path) = file_path(result_file) { |
98 | | if let Some(start_path) = file_path(start) { |
99 | | assert!( |
100 | | result_path.starts_with(start_path), |
101 | | "sandbox escape: start='{:?}' result='{}'", |
102 | | start, |
103 | | result_path.display() |
104 | | ); |
105 | | } |
106 | | } |
107 | | } |
108 | | } |