Skip to content

Commit f7f9f42

Browse files
committed
rename refactor
1 parent 2504c15 commit f7f9f42

3 files changed

Lines changed: 68 additions & 108 deletions

File tree

library/std/src/sys/fs/windows.rs

Lines changed: 61 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,66 @@ pub fn unlink(path: &WCStr) -> io::Result<()> {
13181318
}
13191319
}
13201320

1321+
/// Renames a file using NtSetInformationFile and FileRenameInformation. If to_dir is None, passes a
1322+
/// null pointer, which uses the directory `from` is already in (or it's ignored if `to` is an
1323+
/// absolute path).
1324+
fn nt_rename(from: impl AsHandle, to: &[u16], to_dir: Option<&Handle>) -> io::Result<()> {
1325+
let handle = from.as_handle();
1326+
1327+
const too_long_err: io::Error =
1328+
io::const_error!(io::ErrorKind::InvalidFilename, "Filename too long");
1329+
let struct_size = to
1330+
.len()
1331+
.checked_mul(2)
1332+
.and_then(|x| x.checked_add(offset_of!(c::FILE_RENAME_INFORMATION, FileName)))
1333+
.ok_or(too_long_err)?;
1334+
let layout = Layout::from_size_align(struct_size, align_of::<c::FILE_RENAME_INFORMATION>())
1335+
.map_err(|_| too_long_err)?;
1336+
let struct_size = u32::try_from(struct_size).map_err(|_| too_long_err)?;
1337+
let to_byte_len = u32::try_from(to.len() * 2).map_err(|_| too_long_err)?;
1338+
1339+
let file_rename_info;
1340+
// SAFETY: We allocate enough memory for a full FILE_RENAME_INFORMATION struct and the filename.
1341+
unsafe {
1342+
file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFORMATION>();
1343+
if file_rename_info.is_null() {
1344+
return Err(io::ErrorKind::OutOfMemory.into());
1345+
}
1346+
1347+
(&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFORMATION_0 {
1348+
Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
1349+
});
1350+
1351+
(&raw mut (*file_rename_info).RootDirectory)
1352+
.write(to_dir.map(Handle::as_raw_handle).unwrap_or_else(ptr::null_mut));
1353+
// Don't include the NULL in the size
1354+
(&raw mut (*file_rename_info).FileNameLength).write(to_byte_len);
1355+
1356+
to.as_ptr().copy_to_nonoverlapping(
1357+
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
1358+
to.len(),
1359+
);
1360+
}
1361+
1362+
let status = unsafe {
1363+
c::NtSetInformationFile(
1364+
handle.as_raw_handle(),
1365+
&mut c::IO_STATUS_BLOCK::default(),
1366+
file_rename_info.cast::<c_void>(),
1367+
struct_size,
1368+
c::FileRenameInformation,
1369+
)
1370+
};
1371+
unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
1372+
if c::nt_success(status) {
1373+
// SAFETY: nt_success guarantees that handle is no longer null
1374+
Ok(())
1375+
} else {
1376+
Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) }))
1377+
}
1378+
.io_result()
1379+
}
1380+
13211381
pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {
13221382
if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {
13231383
let err = api::get_last_error();
@@ -1330,58 +1390,7 @@ pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> {
13301390
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
13311391
let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() };
13321392

1333-
// Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
1334-
// This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1335-
let Ok(new_len_without_nul_in_bytes): Result<u32, _> =
1336-
((new.count_bytes() - 1) * 2).try_into()
1337-
else {
1338-
return Err(err).io_result();
1339-
};
1340-
let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap();
1341-
let struct_size = offset + new_len_without_nul_in_bytes + 2;
1342-
let layout =
1343-
Layout::from_size_align(struct_size as usize, align_of::<c::FILE_RENAME_INFO>())
1344-
.unwrap();
1345-
1346-
let file_rename_info;
1347-
// SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
1348-
unsafe {
1349-
file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFO>();
1350-
if file_rename_info.is_null() {
1351-
return Err(io::ErrorKind::OutOfMemory.into());
1352-
}
1353-
1354-
(&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 {
1355-
Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS
1356-
| c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
1357-
});
1358-
1359-
(&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut());
1360-
// Don't include the NULL in the size
1361-
(&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes);
1362-
1363-
new.as_ptr().copy_to_nonoverlapping(
1364-
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
1365-
new.count_bytes(),
1366-
);
1367-
}
1368-
1369-
let result = unsafe {
1370-
c::SetFileInformationByHandle(
1371-
f.as_raw_handle(),
1372-
c::FileRenameInfoEx,
1373-
file_rename_info.cast::<c_void>(),
1374-
struct_size,
1375-
)
1376-
};
1377-
unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
1378-
if result == 0 {
1379-
if api::get_last_error() == WinError::DIR_NOT_EMPTY {
1380-
return Err(WinError::DIR_NOT_EMPTY).io_result();
1381-
} else {
1382-
return Err(err).io_result();
1383-
}
1384-
}
1393+
nt_rename(f.handle, new.as_slice(), None)?;
13851394
} else {
13861395
return Err(err).io_result();
13871396
}

library/std/src/sys/fs/windows/dir.rs

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
use crate::alloc::{Layout, alloc, dealloc};
2-
use crate::ffi::c_void;
3-
use crate::mem::offset_of;
1+
use super::{debug_path_handle, nt_rename};
42
use crate::os::windows::io::{
53
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle,
64
OwnedHandle, RawHandle,
75
};
86
use crate::path::Path;
97
use crate::sys::api::{self, SetFileInformation, UnicodeStrRef, WinError};
10-
use crate::sys::fs::windows::debug_path_handle;
118
use crate::sys::fs::{File, OpenOptions};
129
use crate::sys::handle::Handle;
1310
use crate::sys::path::{WCStr, with_native_path};
@@ -143,59 +140,8 @@ impl Dir {
143140
opts.access_mode(c::DELETE);
144141
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
145142
let handle = self.open_file_native(from, &opts, dir)?;
146-
// Calculate the layout of the `FILE_RENAME_INFORMATION` we pass to `NtSetInformationFile`
147-
// This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
148-
const too_long_err: io::Error =
149-
io::const_error!(io::ErrorKind::InvalidFilename, "Filename too long");
150-
let struct_size = to
151-
.len()
152-
.checked_mul(2)
153-
.and_then(|x| x.checked_add(offset_of!(c::FILE_RENAME_INFORMATION, FileName)))
154-
.ok_or(too_long_err)?;
155-
let layout = Layout::from_size_align(struct_size, align_of::<c::FILE_RENAME_INFORMATION>())
156-
.map_err(|_| too_long_err)?;
157-
let struct_size = u32::try_from(struct_size).map_err(|_| too_long_err)?;
158-
let to_byte_len = u32::try_from(to.len() * 2).map_err(|_| too_long_err)?;
159-
160-
let file_rename_info;
161-
// SAFETY: We allocate enough memory for a full FILE_RENAME_INFORMATION struct and the filename.
162-
unsafe {
163-
file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFORMATION>();
164-
if file_rename_info.is_null() {
165-
return Err(io::ErrorKind::OutOfMemory.into());
166-
}
167-
168-
(&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFORMATION_0 {
169-
Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
170-
});
171-
172-
(&raw mut (*file_rename_info).RootDirectory).write(to_dir.handle.as_raw_handle());
173-
// Don't include the NULL in the size
174-
(&raw mut (*file_rename_info).FileNameLength).write(to_byte_len);
175-
176-
to.as_ptr().copy_to_nonoverlapping(
177-
(&raw mut (*file_rename_info).FileName).cast::<u16>(),
178-
to.len(),
179-
);
180-
}
181143

182-
let status = unsafe {
183-
c::NtSetInformationFile(
184-
handle.as_raw_handle(),
185-
&mut c::IO_STATUS_BLOCK::default(),
186-
file_rename_info.cast::<c_void>(),
187-
struct_size,
188-
c::FileRenameInformation,
189-
)
190-
};
191-
unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
192-
if c::nt_success(status) {
193-
// SAFETY: nt_success guarantees that handle is no longer null
194-
Ok(())
195-
} else {
196-
Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) }))
197-
}
198-
.io_result()
144+
nt_rename(handle, to, Some(&to_dir.handle))
199145
}
200146
}
201147

library/std/src/sys/path/windows.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ impl WCStr {
3434
self.0.as_ptr()
3535
}
3636

37+
#[allow(unused)]
3738
pub fn count_bytes(&self) -> usize {
3839
self.0.len()
3940
}
41+
42+
pub fn as_slice(&self) -> &[u16] {
43+
&self.0
44+
}
4045
}
4146

4247
#[inline]

0 commit comments

Comments
 (0)