-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Fix fs::remove_dir_all
#31944
Fix fs::remove_dir_all
#31944
Conversation
r? @aturon (rust_highfive has picked a reviewer for you, use r? to override) |
Your PR has three merges. You may want to consider rebasing and force pushing. |
|
||
#[repr(C)] | ||
pub struct FILE_DISPOSITION_INFO { | ||
pub DeleteFile: BOOL, // for true use -1, not TRUE |
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.
Why does this need to be -1? I don't see anything on MSDN for why this can't be TRUE
.
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 struct is the same as FILE_DISPOSITION_INFORMATION which uses a BOOLEAN. But TRUE just doesn't work, it gives an error. The documentation for this fuction is not great, this one is better: https://msdn.microsoft.com/nl-nl/library/windows/hardware/ff567096(v=vs.85).aspx
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.
Set this member to TRUE to delete the file when it is closed.
Nowhere do I see any mention of -1. What error do you get when you use TRUE instead of -1?
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.
Something like: the file name, directory name or volume lable syntax is incorrect (I am on mobile, can't test now). But it took me hours to get working because of this bug. Only afterwards I found out this function is just a very thin wrapper around an NT internal function that takes a BOOLEAN instead of a BOOL, explaining the problem.
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.
Can you move this comment to where it's called down in remove(&self)
and put some of these comments there as well? Sounds like it'll be useful info for the next reader!
@retep998 thanks for helping out with FILE_RENAME_INFO on irc! And time to improve my git skills :( |
On Windows files can have a simple read-only permission flag, and the more complex access rights and access control lists. The read-only flag is one bit of the file attributes. To make sure only the read-only flag is toggled and not the other attributes, read the files attributes and write them back with only this bit changed. By reading and setting the attributes from the handle the file does not have to be opened twice. Also directories can not be read-only, here the flag has a different meaning. So we should no longer report directories as read-only, and also not allow making them read-only.
This fixes the case where `remove_dir_all` is unable to remove large directories on Windows due to race conditions. It is now also able to remove read-only files, and when the paths become longer that MAX_PATH.
Rebased |
opts.access_mode(c::FILE_READ_ATTRIBUTES | c::FILE_WRITE_ATTRIBUTES); | ||
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); | ||
let file = try!(File::open(path, &opts)); | ||
file.set_perm(perm) |
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.
Could you add some tests for this as well? It's fine for some of them to be windows-specific (e.g. in this module), but specifically:
- Directories can't be set to readonly
- Other attributes are preserved
Thanks @pitdicker! This is looking great to me |
Thanks for the review!. |
Updated. I messed up a lot with the permissions stuff of directories. Here is the article that explains how the read-only attribute indicates a directory is special on Windows. As it turns out you can create and delete files in a read-only directory, but nor delete or remove the dir itself. This is exactly the opposite of Unix, where you can't create or delete files in a read-only directory, but can delete the dir itself. So I reversed the permission stuff. And we now have an other edge case to handle on Unix: readonly dirs. Before every call to I think this addresses all the comments except the suggestion to use |
// readonly | ||
mode.set_readonly(false); | ||
try!(set_perm(&child.path(), mode)); | ||
} |
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.
Perhaps this function could take &Metadata
as an argument and perform this logic at the start? That way it could prevent duplication between here/above.
Hm now I'm getting a little more worried with this as time passes, it seems like we're definitely going out of our way to implement a arguably primitive operation, which may end up being surprising to some. This code was originally modelled after Boost which is significantly simpler than what's going on here (even on Unix). That's not necessarily either good or bad, but it's indicative to me that we should tread lightly here. Perhaps we can try to find some other implementations of this function in other libraries? It it standard practice to attempt to so aggressively remove files/directories? |
I think the part in the Windows version of moving files is not really over the top. You could say it is just a necessary evil to avoid races. But I share your feeling about the rest. Personally I am finding more and more functions that could benefit from options. Overwrite or fail if something already exist. Follow symlinks or not. Delete read-only files, fail, or maybe ask for confirmation.... For copy whether we should copy times, attributes etc. Be recursive or not. What to do about read-only files on Windows, and read-only directories on Unix is the difficult part. I kind of like it to just work, as you are also able to move the dir somewhere else and it looks deleted from that place. But I don't feel very strongly about it. (of course it would be sad, as it took me quite some time to get working...) For a filesystem library to take inspiration from, I look a little at AFIO. I can't read it very easily, but it has the focus on correctness, avoiding races, and cross-platform consistency. But it is not all good, otherwise it would have ended up in Boost. |
Yeah it was somewhat planned that In terms of removing readonly directories on Unix, it looks like |
We discussed this during the libs triage meeting yesterday, and we unfortunately didn't reach too many conclusions about this. Some thoughts brought up were:
cc @rust-lang/libs |
The MSVC implementation of |
Interesting! Is the source for that public so we can browse over? |
Well you can view the source if you install MSVC and the Windows SDK.
|
☔ The latest upstream changes (presumably #32390) made this pull request unmergeable. Please resolve the merge conflicts. |
Closing due to inactivity, but feel free to resubmit with a rebase! |
This fixes the case where
remove_dir_all
is unable to remove large directories on Windows due to race conditions.It is now also able to remove read-only files, and when the paths become longer that MAX_PATH.
Fixes #29497
This is based on #31892, I hope I didn't mess up git to much...