Export: protect originals imported through differently named symlinks
Co-Authored-By: OpenAI Codex <noreply@openai.com>
This commit is contained in:
parent
65da36675f
commit
2c94b7fa1f
|
|
@ -58,6 +58,17 @@ impl OriginalGuard {
|
|||
}
|
||||
}
|
||||
}
|
||||
// An imported symlink can have a different name from its original. A write to
|
||||
// that original would change what the library reads through the link. Only do
|
||||
// this fallback for existing targets that the name index did not protect.
|
||||
for (p, what) in self.by_name.values().flatten() {
|
||||
if std::fs::read_link(p).is_ok() && same_file(p, target) {
|
||||
return Err(format!(
|
||||
"{} is {what} in the library: LightCraft never writes over an original (choose another folder or file name)",
|
||||
target.display()
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,3 +323,29 @@ fn a_failed_export_write_keeps_the_previous_file() {
|
|||
assert_ne!(std::fs::read(&prev).unwrap(), b"last week's export");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn export_protects_the_target_of_an_imported_symlink() {
|
||||
use crate::export::{Conflict, Destination};
|
||||
let (original_session, _, dir, src, original) = library_with_jpeg("export-symlink-original");
|
||||
drop(original_session);
|
||||
let alias = dir.join("alias.jpg");
|
||||
std::os::unix::fs::symlink(&src, &alias).unwrap();
|
||||
let sidecar = dir.join("metadata.xmp");
|
||||
std::fs::write(&sidecar, b"<x/>").unwrap();
|
||||
std::os::unix::fs::symlink(&sidecar, alias.with_extension("xmp")).unwrap();
|
||||
let mut s = Session::new().with_fs();
|
||||
s.execute("library.import", &json!({"paths": [alias.to_string_lossy()]})).unwrap();
|
||||
let id = s.catalog.photos().next().unwrap().id;
|
||||
s.execute("develop.set", &json!({"ids": [id.0], "control": "light.exposure", "value": 1.0})).unwrap();
|
||||
let o = ExportOptions { conflict: Conflict::Overwrite, ..Default::default() };
|
||||
let to = Destination { dir: String::new(), exact: Some(src.to_string_lossy().to_string()) };
|
||||
let result = disk_batch(&mut s, id, &o, &to);
|
||||
assert!(result.is_err(), "export replaced the original through its imported alias: {result:?}");
|
||||
assert_eq!(std::fs::read(&src).unwrap(), original);
|
||||
assert!(s.execute("export.checkTarget", &json!({"path": src.to_string_lossy()})).is_err());
|
||||
assert!(s.execute("export.checkTarget", &json!({"path": sidecar.to_string_lossy()})).is_err());
|
||||
assert_eq!(std::fs::read(&sidecar).unwrap(), b"<x/>");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue