-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Use a temporary file for writes #9236
Conversation
92a5907
to
ea7899d
Compare
I.plementing this correctly is going to be a lot harder. If you create a new temporary field all file metadata (timestamps, permissions, owner, groups,..) need to be copied to the new file. That code is highly platform dependent (not supported by std) and not easy to implement |
Gotcha, I'll look into it. I think that the only thing that is platform-dependent is owners/groups and permissions. Unix seems simple, I don't know about Windows though (Vim doesn't try for Windows with its undofiles). |
I'll be punting this to the backburner for now. I'd have to work with |
How would you feel about restricting this to only Unix systems? I wrote code to copy permissions on Windows: fn chown(from: &Path, to: &Path) -> std::io::Result<()> {
use std::os::windows::io::AsRawHandle;
// CHECK: Requires read
let handle = HANDLE(std::fs::File::open(from)?.as_raw_handle() as isize);
let mut owner = PSID::default();
let mut group = PSID::default();
unsafe {
windows::Win32::Security::Authorization::GetSecurityInfo(
handle,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
Some(&mut owner),
Some(&mut group),
None,
None,
None,
)?;
}
// CHECK: requires write
let handle = HANDLE(std::fs::File::open(to)?.as_raw_handle() as isize);
unsafe {
windows::Win32::Security::Authorization::SetSecurityInfo(
handle,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
owner,
group,
None,
None,
)?;
}
Ok(())
} But aside from the questionable safety of what I've written, I'd rather not have to maintain it since this kind of stuff is sparsely documented in the API. |
I took a look at what vim does... It's not easy tgrwap but what I found is that:
|
c3fc081
to
cff812f
Compare
the integration tests fail for me locally toso its not just a permission issue in the runner |
I am not sure if you are quite using tmp file correctly. The following two caveats seem extremely relevant:
I am pretty sure mky tmpfiles are in a separate btrfs partition so that's why its not working |
I think you will want to use https://docs.rs/tempfile/latest/tempfile/struct.Builder.html#method.tempfile_in to create the tomprary file in the same dirtory as the destination file. To avoid creating a bunch of random files you can set the prefix to the filename (including extension) and the suffix to |
I don't think the chown stuff ist actually the problem. We ignore errors so I don't see how that could block writing the file |
and two test failures are simple bugs in the testing infrastructure keeping the file handle open and hence reading from the closed file |
ok I did those changes myself (hope that's ok its separate commits so nothing was lost) locally integration tests pass let's see if CI is green. I think this should handle the subtleties for the most part |
I can test on my Windows machine since I can replicate it in integration tests, if you'd like? I'm also trying to debug it on my end. |
|
yeah I had to stop working due to a meeting. I changed the code to move the old file to a backup and then write to the intended path (backup file is only deleted on successful write). This is how vim handles this and should remove the permission concerns from copy_metadata (since we won't need to move the file afterwards it doesn't matter if we don't have permissions to access it) I believe that the code is correct now and the integration tests just fail because of some tests bugs. Potentially the |
ah looks like you fixed it already, great work! I undid my debug changes which made the CI fail and I noticed that we should be flusing/syncing the old file should not actually be needed since we are replacing the file (so a flush on the old file won't do much good). Lets' see if this passes CI it works locally at least (on linux but seeing that you fixed windows CI I doubt that this would regress that) |
if CI passes we just need to log (with log::error) all the IO error we are currently ignoring then I think this should be good to go |
Before this is ready to merge, I want to port the |
yeah in ead of bailing out on error we should just log all of them with log::errors. Using windows-sys would be great 👍 Thanks for working on the low level stuff that is pretty gnarly. One thing vim does that we don't is that |
its not critical to handle the readonly stuff in this PR since we didn't do anything about that currently either so if that turns out to be more complicated then just leave it for future work |
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.
(rebased as there were just some minor conflicts in the integration tests)
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.
(ups)
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.
Nice work @kirawi & @pascalkuthe !
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Co-authored-by: Pascal Kuthe <[email protected]>
Supersedes #4276 and resolves #3967
I initially suspected it had to do with not seeking to the beginning of the file, but it turned out to be something equally as dumb:
rename
only changes the path of the open inode to the new path, so any open handles to the old inode are not affected. Thus, I had to reopen the temporary file after each write.rename
can replace readonly files if the directory has write permissions. So failing if the file at the path is readonly remains the last task for the tests to pass. Actually, it would be good to also apply the same permissions and everything to the new file as well.Bear in mind I was unaware of even what an inode was until 5 minutes ago, so I might be wrong.