Skip to content

Commit 23ceef1

Browse files
committed
fix(desktop): use native retained-handle rename
1 parent 13df58a commit 23ceef1

2 files changed

Lines changed: 54 additions & 40 deletions

File tree

apps/desktop/src-tauri/src/file_sync_attachment_publication.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,16 +1132,18 @@ fn publish_exact_stage(
11321132
) -> io::Result<()> {
11331133
use std::os::windows::ffi::OsStrExt as _;
11341134
use std::os::windows::io::AsRawHandle as _;
1135-
use windows_sys::Win32::Storage::FileSystem::{
1136-
FileRenameInfo, SetFileInformationByHandle, FILE_RENAME_INFO,
1135+
use windows_sys::Wdk::Storage::FileSystem::{
1136+
FileRenameInformation, NtSetInformationFile, FILE_RENAME_INFORMATION,
11371137
};
1138+
use windows_sys::Win32::Foundation::RtlNtStatusToDosError;
1139+
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
11381140

11391141
let target = target_name.encode_wide().collect::<Vec<_>>();
1140-
// SetFileInformationByHandle requires the variable-length record to retain
1141-
// the complete fixed FILE_RENAME_INFO, including its x64 trailing padding.
1142+
// NtSetInformationFile consumes the native variable-length record while
1143+
// preserving the retained RootDirectory-relative namespace operation.
11421144
// FileNameLength still describes only the UTF-16 payload copied at the
11431145
// FileName field below.
1144-
let fixed = std::mem::size_of::<FILE_RENAME_INFO>();
1146+
let fixed = std::mem::size_of::<FILE_RENAME_INFORMATION>();
11451147
let target_bytes = target
11461148
.len()
11471149
.checked_mul(std::mem::size_of::<u16>())
@@ -1153,11 +1155,12 @@ fn publish_exact_stage(
11531155
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "target name is too long"))?;
11541156
let target_bytes = u32::try_from(target_bytes)
11551157
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "target name is too long"))?;
1156-
// `FILE_RENAME_INFO` has pointer alignment. A byte vector does not promise
1157-
// enough alignment for the typed header.
1158+
// `FILE_RENAME_INFORMATION` has pointer alignment. A byte vector does not
1159+
// promise enough alignment for the typed header.
11581160
let words = buffer_bytes.div_ceil(std::mem::size_of::<usize>());
11591161
let mut buffer = vec![0_usize; words];
1160-
let information = buffer.as_mut_ptr().cast::<FILE_RENAME_INFO>();
1162+
let information = buffer.as_mut_ptr().cast::<FILE_RENAME_INFORMATION>();
1163+
let mut status_block = IO_STATUS_BLOCK::default();
11611164
unsafe {
11621165
(*information).Anonymous.ReplaceIfExists = false;
11631166
(*information).RootDirectory = attachments_directory.handle.as_raw_handle();
@@ -1167,14 +1170,17 @@ fn publish_exact_stage(
11671170
(*information).FileName.as_mut_ptr(),
11681171
target.len(),
11691172
);
1170-
if SetFileInformationByHandle(
1173+
let status = NtSetInformationFile(
11711174
stage.as_raw_handle(),
1172-
FileRenameInfo,
1175+
&mut status_block,
11731176
information.cast(),
11741177
information_bytes,
1175-
) == 0
1176-
{
1177-
return Err(io::Error::last_os_error());
1178+
FileRenameInformation,
1179+
);
1180+
if status < 0 {
1181+
return Err(io::Error::from_raw_os_error(
1182+
RtlNtStatusToDosError(status) as i32,
1183+
));
11781184
}
11791185
}
11801186
Ok(())
@@ -1337,7 +1343,7 @@ mod tests {
13371343
}
13381344

13391345
#[test]
1340-
fn windows_exact_stage_rename_buffer_includes_the_full_fixed_record() {
1346+
fn windows_exact_stage_rename_uses_the_native_handle_api() {
13411347
let source = include_str!("file_sync_attachment_publication.rs").replace("\r\n", "\n");
13421348
let windows_rename = source
13431349
.split_once("#[cfg(windows)]\nfn publish_exact_stage")
@@ -1350,11 +1356,12 @@ mod tests {
13501356
.0;
13511357

13521358
assert!(windows_rename.contains(
1353-
"let fixed = std::mem::size_of::<FILE_RENAME_INFO>();"
1354-
));
1355-
assert!(!windows_rename.contains(
1356-
"let fixed = std::mem::offset_of!(FILE_RENAME_INFO, FileName);"
1359+
"let fixed = std::mem::size_of::<FILE_RENAME_INFORMATION>();"
13571360
));
1361+
assert!(windows_rename.contains("NtSetInformationFile("));
1362+
assert!(windows_rename.contains("FileRenameInformation"));
1363+
assert!(windows_rename.contains("RtlNtStatusToDosError(status)"));
1364+
assert!(!windows_rename.contains("SetFileInformationByHandle("));
13581365
assert!(windows_rename.contains("(*information).FileName.as_mut_ptr()"));
13591366
}
13601367

apps/desktop/src-tauri/src/sync.rs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6996,18 +6996,18 @@ mod tests {
69966996

69976997
#[cfg(target_os = "windows")]
69986998
{
6999-
use windows_sys::Win32::Storage::FileSystem::FILE_RENAME_INFO;
6999+
use windows_sys::Wdk::Storage::FileSystem::FILE_RENAME_INFORMATION;
70007000

70017001
assert!(
7002-
std::mem::offset_of!(FILE_RENAME_INFO, FileName)
7003-
< std::mem::size_of::<FILE_RENAME_INFO>(),
7002+
std::mem::offset_of!(FILE_RENAME_INFORMATION, FileName)
7003+
< std::mem::size_of::<FILE_RENAME_INFORMATION>(),
70047004
"the full fixed rename record includes padding beyond the name field offset"
70057005
);
70067006
}
70077007
}
70087008

70097009
#[test]
7010-
fn windows_retained_rename_buffer_includes_the_full_fixed_record() {
7010+
fn windows_retained_rename_uses_the_native_handle_api() {
70117011
let source = include_str!("sync.rs").replace("\r\n", "\n");
70127012
let windows_rename = source
70137013
.split_once("#[cfg(target_os = \"windows\")]\nfn retained_root_rename")
@@ -7018,11 +7018,12 @@ mod tests {
70187018
.0;
70197019

70207020
assert!(windows_rename.contains(
7021-
"let fixed = std::mem::size_of::<FILE_RENAME_INFO>();"
7022-
));
7023-
assert!(!windows_rename.contains(
7024-
"let fixed = std::mem::offset_of!(FILE_RENAME_INFO, FileName);"
7021+
"let fixed = std::mem::size_of::<FILE_RENAME_INFORMATION>();"
70257022
));
7023+
assert!(windows_rename.contains("NtSetInformationFile("));
7024+
assert!(windows_rename.contains("FileRenameInformation"));
7025+
assert!(windows_rename.contains("RtlNtStatusToDosError(status)"));
7026+
assert!(!windows_rename.contains("SetFileInformationByHandle("));
70267027
assert!(windows_rename.contains("(*information).FileName.as_mut_ptr()"));
70277028
}
70287029

@@ -14593,17 +14594,19 @@ fn retained_root_rename(
1459314594
) -> std::io::Result<()> {
1459414595
use std::os::windows::ffi::OsStrExt as _;
1459514596
use std::os::windows::io::AsRawHandle as _;
14596-
use windows_sys::Win32::Storage::FileSystem::{
14597-
FileRenameInfo, SetFileInformationByHandle, FILE_RENAME_INFO,
14597+
use windows_sys::Wdk::Storage::FileSystem::{
14598+
FileRenameInformation, NtSetInformationFile, FILE_RENAME_INFORMATION,
1459814599
};
14600+
use windows_sys::Win32::Foundation::RtlNtStatusToDosError;
14601+
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
1459914602

1460014603
let source = retained_root_open(directory, source, true, false, false)?;
1460114604
let target = destination.encode_wide().collect::<Vec<_>>();
14602-
// SetFileInformationByHandle requires the variable-length record to retain
14603-
// the complete fixed FILE_RENAME_INFO, including its x64 trailing padding.
14605+
// NtSetInformationFile consumes the native variable-length record while
14606+
// preserving the retained RootDirectory-relative namespace operation.
1460414607
// FileNameLength still describes only the UTF-16 payload copied at the
1460514608
// FileName field below.
14606-
let fixed = std::mem::size_of::<FILE_RENAME_INFO>();
14609+
let fixed = std::mem::size_of::<FILE_RENAME_INFORMATION>();
1460714610
let target_bytes = target
1460814611
.len()
1460914612
.checked_mul(std::mem::size_of::<u16>())
@@ -14632,11 +14635,12 @@ fn retained_root_rename(
1463214635
)
1463314636
})?;
1463414637
let words = buffer_bytes.div_ceil(std::mem::size_of::<usize>());
14635-
// `FILE_RENAME_INFO` has pointer alignment; a byte vector does not promise
14636-
// enough alignment for the typed header even though system allocators often
14637-
// happen to provide it.
14638+
// `FILE_RENAME_INFORMATION` has pointer alignment; a byte vector does not
14639+
// promise enough alignment for the typed header even though system allocators
14640+
// often happen to provide it.
1463814641
let mut buffer = vec![0_usize; words];
14639-
let information = buffer.as_mut_ptr().cast::<FILE_RENAME_INFO>();
14642+
let information = buffer.as_mut_ptr().cast::<FILE_RENAME_INFORMATION>();
14643+
let mut status_block = IO_STATUS_BLOCK::default();
1464014644
before_mutation()?;
1464114645
unsafe {
1464214646
(*information).Anonymous.ReplaceIfExists = replace;
@@ -14647,14 +14651,17 @@ fn retained_root_rename(
1464714651
(*information).FileName.as_mut_ptr(),
1464814652
target.len(),
1464914653
);
14650-
if SetFileInformationByHandle(
14654+
let status = NtSetInformationFile(
1465114655
source.as_raw_handle(),
14652-
FileRenameInfo,
14656+
&mut status_block,
1465314657
information.cast(),
1465414658
information_bytes,
14655-
) == 0
14656-
{
14657-
return Err(std::io::Error::last_os_error());
14659+
FileRenameInformation,
14660+
);
14661+
if status < 0 {
14662+
return Err(std::io::Error::from_raw_os_error(
14663+
RtlNtStatusToDosError(status) as i32,
14664+
));
1465814665
}
1465914666
}
1466014667
Ok(())

0 commit comments

Comments
 (0)