1#[cfg(all(racy_asserts, not(windows)))]
4use crate::fs::append_dir_suffix;
5use crate::fs::rename_impl;
6use std::path::Path;
7use std::{fs, io};
8#[cfg(racy_asserts)]
9use {
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#[cfg_attr(not(racy_asserts), allow(clippy::let_and_return))]
21#[inline]
22pub fn rename(
23 old_start: &fs::File,
24 old_path: &Path,
25 new_start: &fs::File,
26 new_path: &Path,
27) -> io::Result<()> {
28 #[cfg(racy_asserts)]
29 let (old_metadata_before, new_metadata_before) = (
30 stat_unchecked(old_start, old_path, FollowSymlinks::No),
31 stat_unchecked(new_start, new_path, FollowSymlinks::No),
32 );
33
34 let result = rename_impl(old_start, old_path, new_start, new_path);
36
37 #[cfg(racy_asserts)]
38 let (old_metadata_after, new_metadata_after) = (
39 stat_unchecked(old_start, old_path, FollowSymlinks::No),
40 stat_unchecked(new_start, new_path, FollowSymlinks::No),
41 );
42
43 #[cfg(racy_asserts)]
44 check_rename(
45 old_start,
46 old_path,
47 new_start,
48 new_path,
49 &old_metadata_before,
50 &new_metadata_before,
51 &result,
52 &old_metadata_after,
53 &new_metadata_after,
54 );
55
56 result
57}
58
59#[cfg(racy_asserts)]
60#[allow(clippy::too_many_arguments)]
61#[allow(clippy::enum_glob_use)]
62fn 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 }
109 other => panic!(
110 "unsandboxed rename success:\n{:#?}\n{:?} {:?}",
111 other, kind, message
112 ),
113 },
114 (Err((_old_canon_kind, _old_canon_message)), _) => {
115 }
120 (_, Err((_new_canon_kind, _new_canon_message))) => {
121 }
126 },
127
128 _other => {
129 }
141 }
142}
143
144#[cfg(racy_asserts)]
145fn canonicalize_for_rename(start: &fs::File, path: &Path) -> io::Result<PathBuf> {
146 let mut canon = manually::canonicalize_with(start, path, FollowSymlinks::No)?;
147
148 #[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}