Skip to content

Commit aaa0788

Browse files
committed
fix(desktop): size Windows rename records fully
1 parent bede129 commit aaa0788

2 files changed

Lines changed: 54 additions & 11 deletions

File tree

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,10 +1137,11 @@ fn publish_exact_stage(
11371137
};
11381138

11391139
let target = target_name.encode_wide().collect::<Vec<_>>();
1140-
// `FILE_RENAME_INFO` is a variable-length record. Its x64 Rust layout has
1141-
// trailing padding after `FileName`; Windows expects the payload length to
1142-
// begin at the field offset, not at `size_of - 2`.
1143-
let fixed = std::mem::offset_of!(FILE_RENAME_INFO, FileName);
1140+
// SetFileInformationByHandle requires the variable-length record to retain
1141+
// the complete fixed FILE_RENAME_INFO, including its x64 trailing padding.
1142+
// FileNameLength still describes only the UTF-16 payload copied at the
1143+
// FileName field below.
1144+
let fixed = std::mem::size_of::<FILE_RENAME_INFO>();
11441145
let target_bytes = target
11451146
.len()
11461147
.checked_mul(std::mem::size_of::<u16>())
@@ -1335,6 +1336,28 @@ mod tests {
13351336
format!("{:x}", Sha256::digest(bytes))
13361337
}
13371338

1339+
#[test]
1340+
fn windows_exact_stage_rename_buffer_includes_the_full_fixed_record() {
1341+
let source = include_str!("file_sync_attachment_publication.rs").replace("\r\n", "\n");
1342+
let windows_rename = source
1343+
.split_once("#[cfg(windows)]\nfn publish_exact_stage")
1344+
.expect("Windows exact-stage rename")
1345+
.1
1346+
.split_once(
1347+
"\n#[cfg(not(any(target_os = \"linux\", target_os = \"macos\", windows)))]",
1348+
)
1349+
.expect("end of exact-stage rename implementations")
1350+
.0;
1351+
1352+
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);"
1357+
));
1358+
assert!(windows_rename.contains("(*information).FileName.as_mut_ptr()"));
1359+
}
1360+
13381361
#[test]
13391362
fn reservation_is_durable_before_scratch_creation_and_empty_recovery_clears_it() {
13401363
let (_temp, data_dir, sync_root, target) = fixture();

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

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,7 +5527,7 @@ mod tests {
55275527

55285528
#[test]
55295529
fn sync_folder_probe_and_real_write_share_one_atomic_write_helper() {
5530-
let source = include_str!("sync.rs");
5530+
let source = include_str!("sync.rs").replace("\r\n", "\n");
55315531
let helper_name = ["atomic_retained_tmp_write_then_", "rename_with"].concat();
55325532
let helper_call = format!("{helper_name}(");
55335533
assert_eq!(
@@ -7001,11 +7001,31 @@ mod tests {
70017001
assert!(
70027002
std::mem::offset_of!(FILE_RENAME_INFO, FileName)
70037003
< std::mem::size_of::<FILE_RENAME_INFO>(),
7004-
"rename buffers must start their variable name at the field offset"
7004+
"the full fixed rename record includes padding beyond the name field offset"
70057005
);
70067006
}
70077007
}
70087008

7009+
#[test]
7010+
fn windows_retained_rename_buffer_includes_the_full_fixed_record() {
7011+
let source = include_str!("sync.rs").replace("\r\n", "\n");
7012+
let windows_rename = source
7013+
.split_once("#[cfg(target_os = \"windows\")]\nfn retained_root_rename")
7014+
.expect("Windows retained-root rename")
7015+
.1
7016+
.split_once("\n#[cfg(not(any(unix, target_os = \"windows\")))]")
7017+
.expect("end of retained-root rename implementations")
7018+
.0;
7019+
7020+
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);"
7025+
));
7026+
assert!(windows_rename.contains("(*information).FileName.as_mut_ptr()"));
7027+
}
7028+
70097029
#[test]
70107030
fn windows_directory_parser_rejects_malformed_next_offsets() {
70117031
assert_eq!(
@@ -14579,11 +14599,11 @@ fn retained_root_rename(
1457914599

1458014600
let source = retained_root_open(directory, source, true, false, false)?;
1458114601
let target = destination.encode_wide().collect::<Vec<_>>();
14582-
// `FILE_RENAME_INFO` is a variable-length record. In particular, the x64
14583-
// Rust layout has trailing padding after `FileName`, so `size_of - 2`
14584-
// overstates the fixed prefix accepted by SetFileInformationByHandle and
14585-
// makes an otherwise valid retained-root rename fail with ERROR_INVALID_PARAMETER.
14586-
let fixed = std::mem::offset_of!(FILE_RENAME_INFO, FileName);
14602+
// SetFileInformationByHandle requires the variable-length record to retain
14603+
// the complete fixed FILE_RENAME_INFO, including its x64 trailing padding.
14604+
// FileNameLength still describes only the UTF-16 payload copied at the
14605+
// FileName field below.
14606+
let fixed = std::mem::size_of::<FILE_RENAME_INFO>();
1458714607
let target_bytes = target
1458814608
.len()
1458914609
.checked_mul(std::mem::size_of::<u16>())

0 commit comments

Comments
 (0)