~james/reorder

fix(store): replace generated artefacts atomically

commit
17d8688e666f8398bc8ac7bd195afd91706c28bd browse tree
parent
908ea7335d65c52f45e8387f6c42e462b25a624a
author
james <j@cpl0.zip>
signature
Verified ~james signed with a key registered by ~james
date
2026-10-20 17:12 UTC

2 files changed, +26 −3

check.ml modified

@@ -11,6 +11,23 @@ let rejects action message =
1111
1212 let read name = Syntax.load ("cases/" ^ name ^ ".sexp")
1313
14+let storage folder =
15+ let folder = Filename.concat folder "storage" in
16+ let path = Filename.concat folder "nested/artefact" in
17+ Store.write path "first";
18+ let previous = open_in_bin path in
19+ Fun.protect ~finally:(fun () -> close_in_noerr previous) (fun () ->
20+ Store.write path "replacement";
21+ require (Store.read path = "replacement") "artefact replacement";
22+ require (really_input_string previous 5 = "first") "open reader retains previous artefact");
23+ let target = Filename.concat folder "directory" in
24+ Store.mkdir target;
25+ let failed = try Store.write target "invalid"; false with Sys_error _ -> true in
26+ require failed "directory cannot be replaced by an artefact";
27+ require (Sys.is_directory target) "failed replacement preserves destination";
28+ require (List.sort compare (Array.to_list (Sys.readdir folder)) = ["directory"; "nested"])
29+ "failed replacement removes temporary file"
30+
1431 let laws () =
1532 let source = Layout.generate ~timeout:300000 ^ Store.read "smt/machine.smt2" ^
1633 "\n" ^ Store.read "smt/laws.smt2" in
@@ -155,6 +172,7 @@ let () =
155172 let started = Unix.gettimeofday () in
156173 let folder = Filename.concat (Filename.get_temp_dir_name ()) (Printf.sprintf "reorder%d" (Unix.getpid ())) in
157174 Store.mkdir folder;
175+ storage folder;
158176 codec ();
159177 laws ();
160178 fixtures ();

store.ml modified

@@ -11,9 +11,14 @@ let rec mkdir path =
1111
1212 let write path text =
1313 mkdir (Filename.dirname path);
14- let channel = open_out_bin path in
15- Fun.protect ~finally:(fun () -> close_out_noerr channel) (fun () ->
16- output_string channel text)
14+ let temporary, channel = Filename.open_temp_file ~mode:[Open_binary]
15+ ~temp_dir:(Filename.dirname path) ".reorder" ".tmp" in
16+ Fun.protect ~finally:(fun () ->
17+ close_out_noerr channel;
18+ if Sys.file_exists temporary then Sys.remove temporary) (fun () ->
19+ output_string channel text;
20+ close_out channel;
21+ Sys.rename temporary path)
1722
1823 let root () =
1924 let rec find path =