A real FileStream constructed from a SafeFileHandle adopts the handle: the file is not opened a second time, no new file share is taken, and the stream can therefore never conflict with whatever already holds the file open.
FileStreamFactoryMock instead resolves the handle to a path through ISafeFileHandleStrategy and opens that path again, taking a fresh file share from SafeFileHandleMock.Share. Layering a stream on a handle can consequently fail with a sharing violation where the real file system succeeds.
Reproduction
MockFileSystem fileSystem = new(o => o.SimulatingOperatingSystem(SimulationMode.Windows));
fileSystem.File.WriteAllText("file.txt", "some content");
fileSystem.WithSafeFileHandleStrategy(
new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("file.txt")));
SafeFileHandle handle = new(new IntPtr(0x1234), ownsHandle: false);
// something else holds the file open exclusively
using FileSystemStream exclusive = fileSystem.File.Open("file.txt",
FileMode.Open, FileAccess.Read, FileShare.None);
using FileSystemStream adopted = fileSystem.FileStream.New(handle, FileAccess.Read);
// IOException: The process cannot access the file ... because it is being used by another process.
A real FileStream over a real handle succeeds here, because it takes no share.
Expected
IFileStreamFactory.New(SafeFileHandle, ...) — all three overloads — should not take a file share, matching the adoption semantics of the underlying type.
Note on ignoreFileShare
IStorageContainer.RequestAccess already has an ignoreFileShare parameter, but it is a no-op on Windows: FileHandle.GrantAccess only consults it inside if (!_fileSystem.Execute.IsWindows). That may be deliberate, so rather than change its meaning — three call sites in InMemoryStorage rely on it — the attached PR skips taking a share altogether for the adopting case, using the existing FileHandle.Ignore.
Worth deciding separately whether ignoreFileShare being inert on Windows is itself a bug.
A real
FileStreamconstructed from aSafeFileHandleadopts the handle: the file is not opened a second time, no new file share is taken, and the stream can therefore never conflict with whatever already holds the file open.FileStreamFactoryMockinstead resolves the handle to a path throughISafeFileHandleStrategyand opens that path again, taking a fresh file share fromSafeFileHandleMock.Share. Layering a stream on a handle can consequently fail with a sharing violation where the real file system succeeds.Reproduction
A real
FileStreamover a real handle succeeds here, because it takes no share.Expected
IFileStreamFactory.New(SafeFileHandle, ...)— all three overloads — should not take a file share, matching the adoption semantics of the underlying type.Note on
ignoreFileShareIStorageContainer.RequestAccessalready has anignoreFileShareparameter, but it is a no-op on Windows:FileHandle.GrantAccessonly consults it insideif (!_fileSystem.Execute.IsWindows). That may be deliberate, so rather than change its meaning — three call sites inInMemoryStoragerely on it — the attached PR skips taking a share altogether for the adopting case, using the existingFileHandle.Ignore.Worth deciding separately whether
ignoreFileSharebeing inert on Windows is itself a bug.