-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: os: Create/Open/OpenFile() set FILE_SHARE_DELETE on windows #32088
Comments
/cc @alexbrainman |
Why should it? Alex |
FILE_SHARE_DELETE enables the code example above, which works on MacOS & Linux but fails on Windows. It's also necessary for:
|
Why don't we adjust MacOS & Linux code instead?
I don't understand what you are trying to say. Alex |
@networkimprov You must call Remove after Close() on Windows.
|
@alexbrainman when doing reliable file I/O (as for databases), it's standard practice to create a file with a temporary name, write it, fsync it, and rename it. Open files can be renamed or deleted by default on Unix. It seems like an oversight that the Windows flag for this capability is not set. I doubt we'll convince the Go team to change the way it works for Linux & MacOS :-) @mattn pls apply the fix I described and try the code I posted. |
I am fine the way things are now. Alex |
Is there a rationale for omitting this common capability in Windows? Can you provide a switch in syscall_windows.go so that we can select the Unix behavior at program start? |
I'm okay that we add new API or flags. But I have objection to change current behavior. Since FILE_SHARE_DELETE is not same as Unix behavior. #include <windows.h>
#include <stdio.h>
int
main(int argc, char* argv[]) {
HANDLE h = CreateFile("test.txt",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
getchar();
char buf[256] = {0};
DWORD nread;
printf("%d\n", ReadFile(h, buf, 256, &nread, NULL));
printf("%s,%d\n", buf, nread);
return 0;
} Comple this code on Windows, and try to run as The file can be deleted but remaining there while the process exists. So this change will not work well for your expected. |
I realize the directory entry isn't removed but the docs say
So I don't understand your screen log. Anyway, a switch like this would be fine:
|
I’d like to mention that I’ve been bitten by this at work before. Basically we had:
The code ran fine on our unix-based CI platforms, but exploded on Windows. Assuming there aren’t any weird side effects, it would be nice if things just worked. |
IIRC, we've made a few adjustments to GOOS=windows & plan9 behavior in the past to more closely match Unix semantics. I wouldn't mind making this be another such case if the semantics are close enough. @mattn's comment, however, suggests the behavior is not close enough so it might not be worth it. I don't want to see some global option, though. That just seems like a debugging nightmare. |
@bradfitz |
@guybrand writes in the golang-dev thread:
Assuming "uploader" opens the file before "my program" calls os.Remove(), I think you contradicted the Win API docs: DeleteFile marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED. Re "pairing an _open_osfhandle() of the CreateFile to an _fdopen", can you point to example code? |
If we add FILE_SHARE_DELETE, many programmer will mistakenly use it to simulate Unix behavior. In this case, you can make a function separated by build-constraints for each OSs. And it should return |
To retain the functionality of os.OpenFile() this plan appears to require reimplementing all of: os.OpenFile() |
I dont see a contradiction with WIndows API, please point what looks like a contridiction. As for a code sample, I Googled a bit, and found this: BUT |
@networkimprov If you mean "this sounds like other processes with a handle to the file would still be able to read and write to the file", I would bet lunch the second you read write to such a file you do get an "EOF" style error - but again need to confirm to be 100% positive. In either case, my point would be (at this point - I am taking "sides" in the "native like" vs " os-agnostic" point) - even if you implement _fdopen style solution, you would get inconsistency between your service and all other executables you collaborate with, so the use could only be in interaction with other go executables (or rare services that DO use fd's directly). In other words - your app would be the "smartest kid in class" - that no other kid can understand. Do you still think changing this to the default behavior makes sense ? |
I have never considered that question. I do not know. The way Go files work on Windows is consistent with all other developers tools I have used in my life. It would be surprising to me, if Go files would work as you propose. I also suspect, it would break many existing programs. Alex |
@alexbrainman I also suggested a switch to enable it, instead of changing the default. @bradfitz there is syscall.SocketDisableIPv6, that's not really different from a flag to adjust syscall.Open() behavior. |
Since syscall_windows*.go states and type_windows*.go has All is quite ready, the only question is how to implement the flag. Another option would be to use the perm parameter, which is not supported for windows, this may get a little awkward last option I can think of is os.OpenDeletableFile( all the above solutions are simple to implement, a little more time to test (cross os), just need a voter... |
One way to support this behavior without changing the defaults or extending the API surface might be to just accept In any event, I would strongly oppose changing the default behavior. Making |
I haven't really thought about what we should do here, but I want to make one thing clear:
We will not be doing this. That would make it impossible for a single program to use different packages that expect different behavior. |
@havoc-io lets take Liam's code, he so actual sort order is Current windows behavior alerts the developer "this is wrong", developer would probably push the delete to be defer'ed as well How is that diff from Posix? I think we should stop looking for a "same solution" as there are differences we will not resolve within go (case sensitive filenames ? :) we will let Linux 5.2 do that...) Altogether it looks like looking for a solution for a temporary file, if it is, windows supports GetTempFileName which can be considered as a standard solution for a file that is "used and then trashed" . If on the other hand we wish to allow a developer to defer deletes in windows, that's possible, see my suggestions above, but the developer must take responsibility for that, and understand the consciousness - therefore must be a flag with a good name convention. |
The primary use for this feature is to rename an open file after creating it:
I don't know why you wouldn't add a flag os.O_WRNDEL (windows rename/delete; a no-op on other platforms), and document that mode's differences with Unix; that a deleted file remains in its directory, but can't be opened, until os.Close(). Also mention that moving the file to a temp dir before deleting it provides more Unix-like behavior. |
@guybrand I think perhaps you're misunderstanding my proposal. I'm not advocating that
@networkimprov That's essentially what I'm proposing, except that there's no point in defining a new flag since a perfectly valid one already exists: |
@havoc-io sorry for the misunderstanding, this was my takeout from: Adding os.O_WRNDEL matches with my second suggestion apart from not being explicit enough for the developer in terms of "what would be the behavior", perhaps os.WADRNDEL - Windows allow deferred rename/delete) . |
I'm sorry, but that's not how the proposal process works. It is not enough to "refute" other people's arguments. "The goal of the proposal process is to reach general consensus about the outcome in a timely manner." There is no clear consensus about the path forward here. Technical arguments have been made, and they did not convince key Go developers who have made significant contributions to the Windows port (namely, @alexbrainman and @mattn). In addition to the lack of clear consensus, there is no clear sign of urgency to do something today: Go worked fine on Windows for nearly 10 years before this issue was filed. As I understand it, leaving everything alone means Go will continue to work as well as it always has. I filed #34681 to provide an easier way to open files with FILE_SHARE_DELETE in the (still likely) event that this one is declined. It seemed more helpful to start a new thread limited to that idea than to continue this one, which has gotten very long. |
@rsc, before you decline this, let's hear what Alex thinks of your O_ALLOW_DELETE proposal. Early in this discussion, he was against a new os.OpenFile() flag that sets file_share_delete, for the same reasons he disagreed with your os.Create/Open/OpenFile() suggestion. He is concerned that other programs assume that no one ever opens files that way, because MSVC fopen() cannot do so. Those (still unspecified) programs will therefore break Go programs which set the flag. |
If you are prepared to loop the rename, you don't need to change anything in Go repo code. Your program will work as good as it works now.
I think copying code into separate package is fine. While investigating moby/moby#39974 I got the same idea. I don't think there is much code there to maintain. In fact I implemented just that https://github.com/alexbrainman/goissue34681 Feel free to copy or use as is. Maybe, given, there is so much interest in this functionality, you actually create proper package that can be shared and used by others. This way you can keep it up to date and fix bugs.
Please, try https://github.com/alexbrainman/goissue34681 first. If you not satisfied for some reason, we could discuss adding new functionality to Go repo. Thank you. Alex |
I'm really disappointed by this response.
Copying this large swath of code, which is largely not even understood (as
in why is this doing what it's doing) just so we can add a single option
that the system itself supports, just that Go, mostly likely I assume not
even intentionally, doesn't provide a means to pass the option down even to
syscall.Open seems like a rediculous situation.
Why do I have to rewrite syscall.Open to pass this option in?
On Sat, Oct 5, 2019 at 18:43 Alex Brainman ***@***.***> wrote:
Alex, one can loop the rename attempt if outside access is allowed, ...
If you are prepared to loop the rename, you don't need to change anything
in Go repo code. Your program will work as good as it works now.
Right now, the only solution is to literally copy a bunch of code out of
Go's standard library, change one line, and then maintain that fork forever.
I think copying code into separate package is fine. While investigating
moby/moby#39974 <moby/moby#39974> I got the same
idea. I don't think there is much code there to maintain. In fact I
implemented just that
https://github.com/alexbrainman/goissue34681
Feel free to copy or use as is. Maybe, given, there is so much interest in
this functionality, you actually create proper package that can be shared
and used by others. This way you can keep it up to date and fix bugs.
@rsc <https://github.com/rsc>, before you decline this, let's hear what
Alex thinks of your O_ALLOW_DELETE proposal.
Please, try
https://github.com/alexbrainman/goissue34681
first. If you not satisfied for some reason, we could discuss adding new
functionality to Go repo.
Thank you.
Alex
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#32088?email_source=notifications&email_token=AAGDCZXHULQEMHAPTO6ZUJTQNFGE7A5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAN7QIQ#issuecomment-538703906>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAGDCZWYUNMCTAGO567AV73QNFGE7ANCNFSM4HNPNYIA>
.
--
- Brian Goff
|
I am sorry you feel that way. But did you actually tried to use my package? Alex |
@rsc, since Alex is also opposed to an os.OpenFile() flag, should we do nothing? How about putting this feature behind a build flag? As to whether "Go worked fine on Windows for nearly 10 years," it definitely did not, in the event you needed to rename an open file. (It was also broken on Windows 8/10 laptops for the past 7 years.) |
I have my own fork of os.OpenFile and syscall.Open already in moby/moby.
Why are we being so dismissive here?
On Tue, Oct 8, 2019 at 01:47 Alex Brainman ***@***.***> wrote:
I'm really disappointed by this response.
I am sorry you feel that way. But did you actually tried to use my package?
Alex
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#32088?email_source=notifications&email_token=AAGDCZRV72GY4IJQJVJWMYTQNRCIHA5CNFSM4HNPNYIKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEATNATA#issuecomment-539414604>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAGDCZRNYSMIE6BX77XJVWDQNRCIHANCNFSM4HNPNYIA>
.
--
- Brian Goff
|
I'd suggest this specific proposal be declined. We're not going to introduce a bunch of TOCTOU security bugs for Windows users who relied on the existing behavior of os.OpenFile. The larger question here is, "how can we expose to Go users the large variety of interesting flags for Windows' |
@zx2c4 did you read the whole thread? We weren't able to identify a single case of a Go user who relies on the existing, undocumented behavior, despite repeated attempts. |
It's been a week since #32088 (comment), and there is still very much no clear consensus, which means we should decline this. Declined. |
I've posted a summary of options with pros & cons in #34681 (comment). |
On Linux & MacOS we can write this; on Windows it fails with a "sharing violation":
If you develop on Windows and deploy to Linux etc, and your code relies on this undocumented GOOS=windows behavior of os.Rename() & .Remove(), it is broken and perhaps vulnerable. Note that package "os" has fifteen mentions of other Windows-specific behavior.
To fix this, syscall.Open() at https://golang.org/src/syscall/syscall_windows.go#L272
needs
sharemode |= FILE_SHARE_DELETE
Microsoft recommends this be made the default: #32088 (comment)
Rust made it a default: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html
Mingw-w64 made it a default seven years ago:
https://sourceforge.net/p/mingw-w64/code/HEAD/tree/stable/v3.x/mingw-w64-headers/include/ntdef.h#l858
Erlang made it a default over six years ago: erlang/otp@0e02f48
Python couldn't adopt it due to limitations of MSVC runtime: https://bugs.python.org/issue15244
Therefore syscall.Open() should use file_share_delete by default, and syscall should provide both:a) a global switch to disable it (for any existing apps that rely on its absence), and
b) a flag for use with os.OpenFile() to disable it on a specific file handle.
Update after #32088 (comment) by @rsc:
a) os.Create/Open/OpenFile() should always enable file_share_delete on Windows,
b) syscall.Open() on Windows should accept a flag which enables file_share_delete, and
c) syscall on Windows should export a constant for the new flag.
The docs for os.Remove() should also note that to reuse a filename after deleting an open file on Windows, one must do:
os.Rename(path, unique_path); os.Remove(unique_path)
.Win API docs
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-deletefilea
https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilea
If there is a reason not to do this, it should be documented in os.Remove() & .Rename().
cc @alexbrainman
@gopherbot add OS-Windows
The text was updated successfully, but these errors were encountered: