|
|
@ -893,15 +893,18 @@ impl Document {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let write_path = tokio::fs::read_link(&path)
|
|
|
|
|
|
|
|
.await
|
|
|
|
|
|
|
|
.unwrap_or_else(|_| path.clone());
|
|
|
|
|
|
|
|
|
|
|
|
if readonly(&path) {
|
|
|
|
if readonly(&write_path) {
|
|
|
|
bail!(std::io::Error::new(
|
|
|
|
bail!(std::io::Error::new(
|
|
|
|
std::io::ErrorKind::PermissionDenied,
|
|
|
|
std::io::ErrorKind::PermissionDenied,
|
|
|
|
"Path is read only"
|
|
|
|
"Path is read only"
|
|
|
|
));
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let backup = if path.exists() {
|
|
|
|
let backup = if path.exists() {
|
|
|
|
let path_ = path.clone();
|
|
|
|
let path_ = write_path.clone();
|
|
|
|
// hacks: we use tempfile to handle the complex task of creating
|
|
|
|
// hacks: we use tempfile to handle the complex task of creating
|
|
|
|
// non clobbered temporary path for us we don't want
|
|
|
|
// non clobbered temporary path for us we don't want
|
|
|
|
// the whole automatically delete path on drop thing
|
|
|
|
// the whole automatically delete path on drop thing
|
|
|
@ -925,7 +928,7 @@ impl Document {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let write_result: anyhow::Result<_> = async {
|
|
|
|
let write_result: anyhow::Result<_> = async {
|
|
|
|
let mut dst = tokio::fs::File::create(&path).await?;
|
|
|
|
let mut dst = tokio::fs::File::create(&write_path).await?;
|
|
|
|
to_writer(&mut dst, encoding_with_bom_info, &text).await?;
|
|
|
|
to_writer(&mut dst, encoding_with_bom_info, &text).await?;
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -934,14 +937,13 @@ impl Document {
|
|
|
|
if let Some(backup) = backup {
|
|
|
|
if let Some(backup) = backup {
|
|
|
|
if write_result.is_err() {
|
|
|
|
if write_result.is_err() {
|
|
|
|
// restore backup
|
|
|
|
// restore backup
|
|
|
|
let _ = tokio::fs::rename(&backup, &path)
|
|
|
|
let _ = tokio::fs::rename(&backup, &write_path)
|
|
|
|
.await
|
|
|
|
.await
|
|
|
|
.map_err(|e| log::error!("Failed to restore backup on write failure: {e}"));
|
|
|
|
.map_err(|e| log::error!("Failed to restore backup on write failure: {e}"));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// copy metadata and delete backup
|
|
|
|
// copy metadata and delete backup
|
|
|
|
let path_ = path.clone();
|
|
|
|
|
|
|
|
let _ = tokio::task::spawn_blocking(move || {
|
|
|
|
let _ = tokio::task::spawn_blocking(move || {
|
|
|
|
let _ = copy_metadata(&backup, &path_)
|
|
|
|
let _ = copy_metadata(&backup, &write_path)
|
|
|
|
.map_err(|e| log::error!("Failed to copy metadata on write: {e}"));
|
|
|
|
.map_err(|e| log::error!("Failed to copy metadata on write: {e}"));
|
|
|
|
let _ = std::fs::remove_file(backup)
|
|
|
|
let _ = std::fs::remove_file(backup)
|
|
|
|
.map_err(|e| log::error!("Failed to remove backup file on write: {e}"));
|
|
|
|
.map_err(|e| log::error!("Failed to remove backup file on write: {e}"));
|
|
|
|