Skip to content

Commit 2c61d00

Browse files
committed
rename refactor
1 parent 8adc293 commit 2c61d00

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
@@ -1308,6 +1308,66 @@ pub fn unlink(path: &WCStr) -> io::Result<()> {
13081308
}
13091309
}
13101310

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

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

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)