-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Platform-dependent FileStream permissions behavior? #24432
Comments
The implementation of This issue was discussed earlier in #22011 and closed as "platform limitation". |
@atykhyy, how do you propose the implementation be improved? |
If the underlying platform or filesystem does not support locking at all, I'm afraid there is nothing else to do but crash the process, because otherwise the code in it that relies on locking semantics will be running with invalid assumptions, possibly leading to data corruption etc. As for the read/write locks, the situation is bad. It might make marginally more sense to treat |
That has worse problems, e.g. from the man page: |
Sorry, I'd just deleted that. These locks don't provide the necessary granularity either. |
For what it's worth, Linux has "open file description |
This comment has been minimized.
This comment has been minimized.
On further consideration, it seems to be possible in Linux to imitate Windows file-level locking semantics in cooperating .NET Core programs, building on Linux-specific open file description locks. The key here is that one can lock byte ranges beyond the end of the file. This permits one to define "abstract" ranges to communicate granular lock information; such a scheme can even be made more or less compatible with
However, since (a) Unix has no mandatory file locking (Linux sort of does, but it must be turned on on a per-mountpoint and per-file basis and has other problems and limitations; suffice it to say that the |
Triage: need to investigate our current usage of advisory locking to see if we address the comments above. |
The FetchStepCleansUpStaleFetchLock() functional test currently only succeeds on Windows (and is therefore marked with the MacTODO.TestNeedsToLockFile category) because it checks whether the FetchStep command has released its lock file by testing if the lock file has been deleted. This works with our WindowsFileBasedLock implementation, which opens the lock file with FileOptions.DeleteOnClose, so the file is removed when the lock is released. However, the MacFileBasedLock version leaves the file in place and uses flock(2) and LOCK_EX to acquire and release the lock atomically; there is no simple way to delete the file while still holding the open file descriptor needed for flock(). To make this test succeed on Mac/Linux while still being meaningful, we need to be robust against the lock file existing both at the start of the test and afterwards, while still ensuring that the lock has been released by the FetchStep command. We therefore simply attempt to open the lock file after the fetch command completes, using the FileShare.None attribute, which will fail on all platforms if the lock file has not been released. On Windows this is because WindowsFileBasedLock uses FileShare.Read to open the lock file, so if the lock is still held our attempt with FileShare.None will fail. On macOS (and Linux) this is because our use of flock() in MacFileBasedLock will conflict with the flock() with LOCK_EX which the .NET Core Unix implementation of FileShare.None also uses. See the discussions in microsoft/VFSForGit#213, dotnet/runtime#24432, as well as the .NET Core library implementation in: https://github.com/dotnet/runtime/blob/341b8b22280e297daa7d96b4b8a9fa1d40d28 aa9/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs#L73-L8 8 We also rename the test to FetchStepReleasesFetchLockFile() to more accurately reflect what it is now testing.
The FetchStepCleansUpStaleFetchLock() functional test currently only succeeds on Windows (and is therefore marked with the MacTODO.TestNeedsToLockFile category) because it checks whether the FetchStep command has released its lock file by testing if the lock file has been deleted. This works with our WindowsFileBasedLock implementation, which opens the lock file with FileOptions.DeleteOnClose, so the file is removed when the lock is released. However, the MacFileBasedLock version leaves the file in place and uses flock(2) and LOCK_EX to acquire and release the lock atomically; there is no simple way to delete the file while still holding the open file descriptor needed for flock(). To make this test succeed on Mac/Linux while still being meaningful, we need to be robust against the lock file existing both at the start of the test and afterwards, while still ensuring that the lock has been released by the FetchStep command. We therefore simply attempt to open the lock file after the fetch command completes, using the FileShare.None attribute, which will fail on all platforms if the lock file has not been released. On Windows this is because WindowsFileBasedLock uses FileShare.Read to open the lock file, so if the lock is still held our attempt with FileShare.None will fail. On macOS (and Linux) this is because our use of flock() in MacFileBasedLock will conflict with the flock() with LOCK_EX which the .NET Core Unix implementation of FileShare.None also uses. See the discussions in microsoft/VFSForGit#213, dotnet/runtime#24432, as well as the .NET Core library implementation in: https://github.com/dotnet/runtime/blob/341b8b22280e297daa7d96b4b8a9fa1d40d28 aa9/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs#L73-L8 8 We also rename the test to FetchStepReleasesFetchLockFile() to more accurately reflect what it is now testing.
The FetchStepCleansUpStaleFetchLock() functional test currently only succeeds on Windows (and is therefore marked with the MacTODO.TestNeedsToLockFile category) because it checks whether the FetchStep command has released its lock file by testing if the lock file has been deleted. This works with our WindowsFileBasedLock implementation, which opens the lock file with FileOptions.DeleteOnClose, so the file is removed when the lock is released. However, the MacFileBasedLock version leaves the file in place and uses flock(2) and LOCK_EX to acquire and release the lock atomically; there is no simple way to delete the file while still holding the open file descriptor needed for flock(). To make this test succeed on Mac/Linux while still being meaningful, we need to be robust against the lock file existing both at the start of the test and afterwards, while still ensuring that the lock has been released by the FetchStep command. We therefore simply attempt to open the lock file after the fetch command completes, using the FileShare.None attribute, which will fail on all platforms if the lock file has not been released. On Windows this is because WindowsFileBasedLock uses FileShare.Read to open the lock file, so if the lock is still held our attempt with FileShare.None will fail. On macOS (and Linux) this is because our use of flock() in MacFileBasedLock will conflict with the flock() with LOCK_EX which the .NET Core Unix implementation of FileShare.None also uses. See the discussions in microsoft/VFSForGit#213, dotnet/runtime#24432, as well as the .NET Core library implementation in: https://github.com/dotnet/runtime/blob/341b8b22280e297daa7d96b4b8a9fa1d40d28 aa9/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs#L73-L8 8 We also rename the test to FetchStepReleasesFetchLockFile() to more accurately reflect what it is now testing.
The FetchStepCleansUpStaleFetchLock() functional test currently only succeeds on Windows (and is therefore marked with the MacTODO.TestNeedsToLockFile category) because it checks whether the FetchStep command has released its lock file by testing if the lock file has been deleted. This works with our WindowsFileBasedLock implementation, which opens the lock file with FileOptions.DeleteOnClose, so the file is removed when the lock is released. However, the MacFileBasedLock version leaves the file in place and uses flock(2) and LOCK_EX to acquire and release the lock atomically; there is no simple way to delete the file while still holding the open file descriptor needed for flock(). To make this test succeed on Mac/Linux while still being meaningful, we need to be robust against the lock file existing both at the start of the test and afterwards, while still ensuring that the lock has been released by the FetchStep command. We therefore simply attempt to open the lock file after the fetch command completes, using the FileShare.None attribute, which will fail on all platforms if the lock file has not been released. On Windows this is because WindowsFileBasedLock uses FileShare.Read to open the lock file, so if the lock is still held our attempt with FileShare.None will fail. On macOS (and Linux) this is because our use of flock() in MacFileBasedLock will conflict with the flock() with LOCK_EX which the .NET Core Unix implementation of FileShare.None also uses. See the discussions in microsoft/VFSForGit#213, dotnet/runtime#24432, as well as the .NET Core library implementation in: https://github.com/dotnet/runtime/blob/341b8b22280e297daa7d96b4b8a9fa1d40d28aa9/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Unix.cs#L73-L88 We also rename the test to FetchStepReleasesFetchLockFile() to more accurately reflect what it is now testing.
Cross-platform file system feature parity seems to be a nightmare to achieve. After 49604 this is the second feature I'd need in a project that does not exist (yet) :/ If anybody knows about a .NET cross-platform file-based locking implementation - I'm all ears :) (Here's something older, also talking about flock, advisory locking etc. -> https://github.com/sjmelia/flock) Edit: Every dive into cross-platform dev topics involving Linux for me seems to end up being a sad history lesson. Citing http://0pointer.de/blog/projects/locking.html:
Edit2: https://www.samba.org/samba/news/articles/low_point/tale_two_stds_os2.html 😱😱😱 |
I am afraid that such an implementation does not exist as of today. Unix simply offers lower granularity that Windows: it offers an exclusive lock and shared lock, which is translates to "nobody can acquire an exclusive lock while I am holding a shared lock". It does not offer the possibility to say: others can read, but not write to the file. Mono uses in-memory implementation that stores the list of the files and their locks, but it works only for locks within the same process. What works well everywhere is |
Spent a few hours looking into why Switching to |
I got here looking for this for a very different reason. I find myself planning a VFS layer to simulate chroot() for some individual threads (not processes). I find myself now wondering which would be the better case; the dumb thing the APIs currently do or the less dumb thing proposed above so that cooperating process behave as expected. |
@atykhyy That doesn't actually work. I spent some time trying to fill out rest of the table only to discover it's broken. Suppose we have two processes trying to open the file with FileAccess = Read and FileShare = Read.
|
I might be missing something obvious, but it's strange to me that this throws an IOException on Windows but not on Ubuntu:
However, this will throw on both:
Is this expected?
The text was updated successfully, but these errors were encountered: