Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not fail due to races when deleting directories #4529

Merged
merged 3 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ Unreleased
- Fixed a bug where a sandboxed action would fail if it declares no dependencies in
its initial working directory or any directory it `chdir`s into. (#4509, @aalekseyev)

- Fix a crash when clearing temporary directories (#4489, #4529, Andrey Mokhov)

2.9.0 (unreleased)
------------------

Expand Down
17 changes: 13 additions & 4 deletions otherlibs/stdune-unstable/fpath.ml
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,32 @@ let unlink_no_err t =
try unlink t with
| _ -> ()

type clear_dir_result =
| Cleared
| Directory_does_not_exist

let rec clear_dir dir =
match Dune_filesystem_stubs.read_directory_with_kinds dir with
| Error ENOENT -> ()
| Error ENOENT -> Directory_does_not_exist
| Error error ->
raise
(Unix.Unix_error
(error, dir, "Stdune.Path.rm_rf: read_directory_with_kinds"))
| Ok listing ->
List.iter listing ~f:(fun (fn, kind) ->
let fn = Filename.concat dir fn in
(* Note that by the time we reach this point, [fn] might have been
deleted by a concurrent process. Both [rm_rf_dir] and [unlink_no_err]
will tolerate such phantom paths and succeed. *)
match kind with
| Unix.S_DIR -> rm_rf_dir fn
| _ -> unlink fn)
| _ -> unlink_no_err fn);
Cleared

and rm_rf_dir path =
clear_dir path;
Unix.rmdir path
match clear_dir path with
| Cleared -> Unix.rmdir path
| Directory_does_not_exist -> ()

let rm_rf ?(allow_external = false) fn =
if (not allow_external) && not (Filename.is_relative fn) then
Expand Down
7 changes: 6 additions & 1 deletion otherlibs/stdune-unstable/fpath.mli
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ val unlink_no_err : string -> unit

val initial_cwd : string

val clear_dir : string -> unit
type clear_dir_result =
| Cleared
| Directory_does_not_exist

val clear_dir : string -> clear_dir_result

(** If the path does not exist, this function is a no-op. *)
val rm_rf : ?allow_external:bool -> string -> unit
5 changes: 3 additions & 2 deletions otherlibs/stdune-unstable/path.mli
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,12 @@ val unlink_no_err : t -> unit

val link : t -> t -> unit

(** If the path does not exist, this function is a no-op. *)
val rm_rf : ?allow_external:bool -> t -> unit

(** [clear_dir t] deletes all the contents of directory [t] without removing [t]
itself *)
val clear_dir : t -> unit
itself. *)
val clear_dir : t -> Fpath.clear_dir_result

val mkdir_p : ?perms:int -> t -> unit

Expand Down
7 changes: 6 additions & 1 deletion otherlibs/stdune-unstable/temp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ let destroy what fn =
set := Path.Set.remove !set fn

let clear_dir dir =
Path.clear_dir dir;
(match Path.clear_dir dir with
| Cleared -> ()
| Directory_does_not_exist ->
(* We can end up here if the temporary directory has already been cleared,
e.g. manually by the caller of [create Dir]. *)
());
let remove_from_set ~set =
set :=
Path.Set.filter !set ~f:(fun f ->
Expand Down