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

Make mv more atomic by trying rename before deleting dst #55384

Merged
merged 5 commits into from
Aug 8, 2024

Conversation

nhz2
Copy link
Contributor

@nhz2 nhz2 commented Aug 5, 2024

As noted in #41584 and https://discourse.julialang.org/t/safe-overwriting-of-files/117758/3 mv is usually expected to be "best effort atomic".

Currently calling mv with force=true calls checkfor_mv_cp_cptree(src, dst, "moving"; force=true) before renaming. checkfor_mv_cp_cptree will delete dst if exists and isn't the same as src.

If dst is an existing file and julia stops after deleting dst but before doing the rename, dst will be removed but will not be replaced with src.

This PR changes mv with force=true to first try rename, and only delete dst if that fails. Assuming file system support and the first rename works, julia stopping will not lead to dst being removed without being replaced.

This also replaces a stopgap solution from #36638 (comment)

@giordano giordano added the filesystem Underlying file system and functions that use it label Aug 5, 2024
@nhz2 nhz2 marked this pull request as ready for review August 6, 2024 03:44
@nhz2 nhz2 requested review from kpamnany and vtjnash August 6, 2024 14:42
Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great!

@vtjnash vtjnash merged commit 57aef91 into JuliaLang:master Aug 8, 2024
7 checks passed
function _mv_noreplace(src::AbstractString, dst::AbstractString)
# Error if dst exists.
# This check currently has TOCTTOU issues.
checkfor_mv_cp_cptree(src, dst, "moving"; force=false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could add this as a flags argument to libuv uv_fs_rename, since many kernels seem to support it now (and it could be emulated with this TOCTOU in the event the kernel does not)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a discussion about
adding this to rust at rust-lang/libs-team#131

Also, https://man7.org/linux/man-pages/man2/rename.2.html says "RENAME_NOREPLACE requires support from the underlying filesystem." but I don't understand how to check if a specific file system supports this, or exactly what happens if the filesystem doesn't support the flag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably EINVAL:
EINVAL The filesystem does not support one of the flags in flags.

@nhz2 nhz2 deleted the more-atomic-mv branch August 14, 2024 03:26
lazarusA pushed a commit to lazarusA/julia that referenced this pull request Aug 17, 2024
…ng#55384)

As noted in JuliaLang#41584 and
https://discourse.julialang.org/t/safe-overwriting-of-files/117758/3
`mv` is usually expected to be "best effort atomic".

Currently calling `mv` with `force=true` calls
`checkfor_mv_cp_cptree(src, dst, "moving"; force=true)` before renaming.
`checkfor_mv_cp_cptree` will delete `dst` if exists and isn't the same
as `src`.

If `dst` is an existing file and julia stops after deleting `dst` but
before doing the rename, `dst` will be removed but will not be replaced
with `src`.

This PR changes `mv` with `force=true` to first try rename, and only
delete `dst` if that fails. Assuming file system support and the first
rename works, julia stopping will not lead to `dst` being removed
without being replaced.

This also replaces a stopgap solution from
JuliaLang#36638 (comment)
LilithHafner pushed a commit that referenced this pull request Aug 31, 2024
KristofferC pushed a commit that referenced this pull request Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
filesystem Underlying file system and functions that use it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants