-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great!
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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…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)
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
withforce=true
callscheckfor_mv_cp_cptree(src, dst, "moving"; force=true)
before renaming.checkfor_mv_cp_cptree
will deletedst
if exists and isn't the same assrc
.If
dst
is an existing file and julia stops after deletingdst
but before doing the rename,dst
will be removed but will not be replaced withsrc
.This PR changes
mv
withforce=true
to first try rename, and only deletedst
if that fails. Assuming file system support and the first rename works, julia stopping will not lead todst
being removed without being replaced.This also replaces a stopgap solution from #36638 (comment)