aboutsummaryrefslogtreecommitdiff
path: root/crates/nres/src/lib.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-02-12 01:00:46 +0300
committerValentin Popov <valentin@popov.link>2026-02-12 01:00:46 +0300
commit3c06e768d603ea4452fc95cc48951af9270f76a1 (patch)
tree453f9b747788ec4b0a12cf42a3ef5f96c53c254c /crates/nres/src/lib.rs
parent70ed6480c2b2b2ecab4956216c1e8e85b0938b4c (diff)
downloadfparkan-3c06e768d603ea4452fc95cc48951af9270f76a1.tar.xz
fparkan-3c06e768d603ea4452fc95cc48951af9270f76a1.zip
feat: добавить поддержку атомарной замены файлов для Windows и тесты на максимальную длину имени
Diffstat (limited to 'crates/nres/src/lib.rs')
-rw-r--r--crates/nres/src/lib.rs49
1 files changed, 37 insertions, 12 deletions
diff --git a/crates/nres/src/lib.rs b/crates/nres/src/lib.rs
index 1fa3b39..e0631e3 100644
--- a/crates/nres/src/lib.rs
+++ b/crates/nres/src/lib.rs
@@ -651,18 +651,43 @@ fn write_atomic(path: &Path, content: &[u8]) -> Result<()> {
file.flush()?;
drop(file);
- match fs::rename(&tmp_path, path) {
- Ok(()) => Ok(()),
- Err(rename_err) => {
- if path.exists() {
- fs::remove_file(path)?;
- fs::rename(&tmp_path, path)?;
- Ok(())
- } else {
- let _ = fs::remove_file(&tmp_path);
- Err(Error::Io(rename_err))
- }
- }
+ if let Err(err) = replace_file_atomically(&tmp_path, path) {
+ let _ = fs::remove_file(&tmp_path);
+ return Err(Error::Io(err));
+ }
+
+ Ok(())
+}
+
+#[cfg(not(windows))]
+fn replace_file_atomically(src: &Path, dst: &Path) -> std::io::Result<()> {
+ fs::rename(src, dst)
+}
+
+#[cfg(windows)]
+fn replace_file_atomically(src: &Path, dst: &Path) -> std::io::Result<()> {
+ use std::iter;
+ use std::os::windows::ffi::OsStrExt;
+ use windows_sys::Win32::Storage::FileSystem::{
+ MoveFileExW, MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH,
+ };
+
+ let src_wide: Vec<u16> = src.as_os_str().encode_wide().chain(iter::once(0)).collect();
+ let dst_wide: Vec<u16> = dst.as_os_str().encode_wide().chain(iter::once(0)).collect();
+
+ // Replace destination in one OS call, avoiding remove+rename gaps on Windows.
+ let ok = unsafe {
+ MoveFileExW(
+ src_wide.as_ptr(),
+ dst_wide.as_ptr(),
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH,
+ )
+ };
+
+ if ok == 0 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(())
}
}