/rust/registry/src/index.crates.io-6f17d22bba15001f/cap-primitives-3.4.4/src/fs/rename.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! This defines `rename`, the primary entrypoint to sandboxed renaming. |
2 | | |
3 | | #[cfg(all(racy_asserts, not(windows)))] |
4 | | use crate::fs::append_dir_suffix; |
5 | | use crate::fs::rename_impl; |
6 | | use std::path::Path; |
7 | | use std::{fs, io}; |
8 | | #[cfg(racy_asserts)] |
9 | | use { |
10 | | crate::fs::{ |
11 | | manually, map_result, path_requires_dir, rename_unchecked, stat_unchecked, FollowSymlinks, |
12 | | Metadata, |
13 | | }, |
14 | | std::path::PathBuf, |
15 | | }; |
16 | | |
17 | | /// Perform a `renameat`-like operation, ensuring that the resolution of both |
18 | | /// the old and new paths never escape the directory tree rooted at their |
19 | | /// respective starts. |
20 | | #[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))] |
21 | | #[inline] |
22 | 0 | pub fn rename( |
23 | 0 | old_start: &fs::File, |
24 | 0 | old_path: &Path, |
25 | 0 | new_start: &fs::File, |
26 | 0 | new_path: &Path, |
27 | 0 | ) -> io::Result<()> { |
28 | 0 | #[cfg(racy_asserts)] |
29 | 0 | let (old_metadata_before, new_metadata_before) = ( |
30 | 0 | stat_unchecked(old_start, old_path, FollowSymlinks::No), |
31 | 0 | stat_unchecked(new_start, new_path, FollowSymlinks::No), |
32 | 0 | ); |
33 | 0 |
|
34 | 0 | // Call the underlying implementation. |
35 | 0 | let result = rename_impl(old_start, old_path, new_start, new_path); |
36 | 0 |
|
37 | 0 | #[cfg(racy_asserts)] |
38 | 0 | let (old_metadata_after, new_metadata_after) = ( |
39 | 0 | stat_unchecked(old_start, old_path, FollowSymlinks::No), |
40 | 0 | stat_unchecked(new_start, new_path, FollowSymlinks::No), |
41 | 0 | ); |
42 | 0 |
|
43 | 0 | #[cfg(racy_asserts)] |
44 | 0 | check_rename( |
45 | 0 | old_start, |
46 | 0 | old_path, |
47 | 0 | new_start, |
48 | 0 | new_path, |
49 | 0 | &old_metadata_before, |
50 | 0 | &new_metadata_before, |
51 | 0 | &result, |
52 | 0 | &old_metadata_after, |
53 | 0 | &new_metadata_after, |
54 | 0 | ); |
55 | 0 |
|
56 | 0 | result |
57 | 0 | } |
58 | | |
59 | | #[cfg(racy_asserts)] |
60 | | #[allow(clippy::too_many_arguments)] |
61 | | #[allow(clippy::enum_glob_use)] |
62 | | fn check_rename( |
63 | | old_start: &fs::File, |
64 | | old_path: &Path, |
65 | | new_start: &fs::File, |
66 | | new_path: &Path, |
67 | | old_metadata_before: &io::Result<Metadata>, |
68 | | new_metadata_before: &io::Result<Metadata>, |
69 | | result: &io::Result<()>, |
70 | | old_metadata_after: &io::Result<Metadata>, |
71 | | new_metadata_after: &io::Result<Metadata>, |
72 | | ) { |
73 | | use io::ErrorKind::*; |
74 | | |
75 | | match ( |
76 | | map_result(old_metadata_before), |
77 | | map_result(new_metadata_before), |
78 | | map_result(result), |
79 | | map_result(old_metadata_after), |
80 | | map_result(new_metadata_after), |
81 | | ) { |
82 | | ( |
83 | | Ok(old_metadata_before), |
84 | | Err((NotFound, _)), |
85 | | Ok(()), |
86 | | Err((NotFound, _)), |
87 | | Ok(new_metadata_after), |
88 | | ) => { |
89 | | assert_same_file_metadata!(&old_metadata_before, &new_metadata_after); |
90 | | } |
91 | | |
92 | | (_, Ok(new_metadata_before), Err((AlreadyExists, _)), _, Ok(new_metadata_after)) => { |
93 | | assert_same_file_metadata!(&new_metadata_before, &new_metadata_after); |
94 | | } |
95 | | |
96 | | (_, _, Err((kind, message)), _, _) => match ( |
97 | | map_result(&canonicalize_for_rename(old_start, old_path)), |
98 | | map_result(&canonicalize_for_rename(new_start, new_path)), |
99 | | ) { |
100 | | (Ok(old_canon), Ok(new_canon)) => match map_result(&rename_unchecked( |
101 | | old_start, &old_canon, new_start, &new_canon, |
102 | | )) { |
103 | | Err((_unchecked_kind, _unchecked_message)) => { |
104 | | /* TODO: Check error messages. |
105 | | assert_eq!(kind, unchecked_kind); |
106 | | assert_eq!(message, unchecked_message); |
107 | | */ |
108 | | } |
109 | | other => panic!( |
110 | | "unsandboxed rename success:\n{:#?}\n{:?} {:?}", |
111 | | other, kind, message |
112 | | ), |
113 | | }, |
114 | | (Err((_old_canon_kind, _old_canon_message)), _) => { |
115 | | /* TODO: Check error messages. |
116 | | assert_eq!(kind, old_canon_kind); |
117 | | assert_eq!(message, old_canon_message); |
118 | | */ |
119 | | } |
120 | | (_, Err((_new_canon_kind, _new_canon_message))) => { |
121 | | /* TODO: Check error messages. |
122 | | assert_eq!(kind, new_canon_kind); |
123 | | assert_eq!(message, new_canon_message); |
124 | | */ |
125 | | } |
126 | | }, |
127 | | |
128 | | _other => { |
129 | | /* TODO: Check error messages. |
130 | | panic!( |
131 | | "inconsistent rename checks: old_start='{:?}', old_path='{}', new_start='{:?}', \ |
132 | | new_path='{}':\n{:#?}", |
133 | | old_start, |
134 | | old_path.display(), |
135 | | new_start, |
136 | | new_path.display(), |
137 | | other |
138 | | ) |
139 | | */ |
140 | | } |
141 | | } |
142 | | } |
143 | | |
144 | | #[cfg(racy_asserts)] |
145 | | fn canonicalize_for_rename(start: &fs::File, path: &Path) -> io::Result<PathBuf> { |
146 | | let mut canon = manually::canonicalize_with(start, path, FollowSymlinks::No)?; |
147 | | |
148 | | // Rename on paths ending in `.` or `/.` fails due to the directory already |
149 | | // being open. Ensure that this happens on the canonical paths too. |
150 | | #[cfg(not(windows))] |
151 | | if path_requires_dir(path) { |
152 | | canon = append_dir_suffix(path.to_path_buf()); |
153 | | |
154 | | assert!(path_requires_dir(&canon)); |
155 | | } |
156 | | |
157 | | Ok(canon) |
158 | | } |