/// <summary> /// Initializes a new instance of the <see cref="Microsoft.Win32.SafeHandles.SafeFileHandle" /> class with the specified path, creation mode, read/write and sharing permission, the access other SafeFileHandles can have to the same file, additional file options and the allocation size. /// </summary> /// <param name="path">A relative or absolute path for the file that the current <see cref="Microsoft.Win32.SafeHandles.SafeFileHandle" /> instance will encapsulate.</param> /// <param name="mode">One of the enumeration values that determines how to open or create the file. The default value is <see cref="FileMode.Open" /></param> /// <param name="access">A bitwise combination of the enumeration values that determines how the file can be accessed. The default value is <see cref="FileAccess.Read" /></param> /// <param name="share">A bitwise combination of the enumeration values that determines how the file will be shared by processes. The default value is <see cref="FileShare.Read" />.</param> /// <param name="preallocationSize">The initial allocation size in bytes for the file. A positive value is effective only when a regular file is being created, overwritten, or replaced. /// Negative values are not allowed. In other cases (including the default 0 value), it's ignored.</param> /// <param name="options">An object that describes optional <see cref="Microsoft.Win32.SafeHandles.SafeFileHandle" /> parameters to use.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> is <see langword="null" />.</exception> /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is an empty string (""), contains only white space, or contains one or more invalid characters. /// -or- /// <paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in an NTFS environment.</exception> /// <exception cref="T:System.NotSupportedException"><paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in a non-NTFS environment.</exception> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="preallocationSize" /> is negative. /// -or- /// <paramref name="mode" />, <paramref name="access" />, or <paramref name="share" /> contain an invalid value.</exception> /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found, such as when <paramref name="mode" /> is <see cref="FileMode.Truncate" /> or <see cref="FileMode.Open" />, and the file specified by <paramref name="path" /> does not exist. The file must already exist in these modes.</exception> /// <exception cref="T:System.IO.IOException">An I/O error, such as specifying <see cref="FileMode.CreateNew" /> when the file specified by <paramref name="path" /> already exists, occurred. /// -or- /// The disk was full (when <paramref name="preallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file). /// -or- /// The file was too large (when <paramref name="preallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).</exception> /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception> /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception> /// <exception cref="T:System.UnauthorizedAccessException">The <paramref name="access" /> requested is not permitted by the operating system for the specified <paramref name="path" />, such as when <paramref name="access" /> is <see cref="FileAccess.Write" /> or <see cref="FileAccess.ReadWrite" /> and the file or directory is set for read-only access. /// -or- /// <see cref="F:System.IO.FileOptions.Encrypted" /> is specified for <paramref name="options" />, but file encryption is not supported on the current platform.</exception> /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. </exception> public static SafeFileHandle OpenHandle(string path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read, FileShare share = FileShare.Read, FileOptions options = FileOptions.None, long preallocationSize = 0) { FileStreamHelpers.ValidateArguments(path, mode, access, share, bufferSize: 0, options, preallocationSize); return(SafeFileHandle.Open(Path.GetFullPath(path), mode, access, share, options, preallocationSize)); }
internal static unsafe long ReadScatterAtOffset(SafeFileHandle handle, IReadOnlyList <Memory <byte> > buffers, long fileOffset) { MemoryHandle[] handles = new MemoryHandle[buffers.Count]; Span <Interop.Sys.IOVector> vectors = buffers.Count <= IovStackThreshold ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffers.Count]; long result; try { int buffersCount = buffers.Count; for (int i = 0; i < buffersCount; i++) { Memory <byte> buffer = buffers[i]; MemoryHandle memoryHandle = buffer.Pin(); vectors[i] = new Interop.Sys.IOVector { Base = (byte *)memoryHandle.Pointer, Count = (UIntPtr)buffer.Length }; handles[i] = memoryHandle; } fixed(Interop.Sys.IOVector *pinnedVectors = &MemoryMarshal.GetReference(vectors)) { result = Interop.Sys.PReadV(handle, pinnedVectors, buffers.Count, fileOffset); } } finally { foreach (MemoryHandle memoryHandle in handles) { memoryHandle.Dispose(); } } return(FileStreamHelpers.CheckFileCall(result, handle.Path)); }
internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span <byte> buffer, long fileOffset) { if (handle.IsAsync) { return(ReadSyncUsingAsyncHandle(handle, buffer, fileOffset)); } NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); fixed(byte *pinned = &MemoryMarshal.GetReference(buffer)) { if (Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, out int numBytesRead, &overlapped) != 0) { return(numBytesRead); } int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_HANDLE_EOF: // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position : // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" return(numBytesRead); case Interop.Errors.ERROR_BROKEN_PIPE: // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. return(0); default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } }
internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset) { if (buffer.IsEmpty) { return; } if (handle.IsAsync) { WriteSyncUsingAsyncHandle(handle, buffer, fileOffset); return; } NativeOverlapped overlapped = GetNativeOverlappedForSyncHandle(handle, fileOffset); fixed(byte *pinned = &MemoryMarshal.GetReference(buffer)) { if (Interop.Kernel32.WriteFile(handle, pinned, buffer.Length, out int numBytesWritten, &overlapped) != 0) { Debug.Assert(numBytesWritten == buffer.Length); return; } int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_NO_DATA: // EOF on a pipe return; default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } }
internal static unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan <byte> buffer, bool syncUsingOverlapped, NativeOverlapped *overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); int numBytesWritten = 0; int r; fixed(byte *p = &MemoryMarshal.GetReference(buffer)) { r = overlapped == null || syncUsingOverlapped? Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, overlapped) : Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped); } if (r == 0) { errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); return(-1); } else { errorCode = 0; return(numBytesWritten); } }
internal static unsafe int ReadFileNative(SafeFileHandle handle, Span <byte> bytes, bool syncUsingOverlapped, NativeOverlapped *overlapped, out int errorCode) { Debug.Assert(handle != null, "handle != null"); int r; int numBytesRead = 0; fixed(byte *p = &MemoryMarshal.GetReference(bytes)) { r = overlapped == null || syncUsingOverlapped? Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, overlapped) : Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped); } if (r == 0) { errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); if (syncUsingOverlapped && errorCode == Interop.Errors.ERROR_HANDLE_EOF) { // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position : // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" return(numBytesRead); } return(-1); } else { errorCode = 0; return(numBytesRead); } }
/// <summary> /// Initializes a new instance of the <see cref="System.IO.FileStream" /> class with the specified path, creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size, additional file options and the allocation size. /// </summary> /// <param name="path">A relative or absolute path for the file that the current <see cref="System.IO.FileStream" /> instance will encapsulate.</param> /// <param name="options">An object that describes optional <see cref="System.IO.FileStream" /> parameters to use.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> or <paramref name="options" /> is <see langword="null" />.</exception> /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is an empty string (""), contains only white space, or contains one or more invalid characters. /// -or- /// <paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in an NTFS environment.</exception> /// <exception cref="T:System.NotSupportedException"><paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in a non-NTFS environment.</exception> /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found, such as when <see cref="System.IO.FileStreamOptions.Mode" /> is <see langword="FileMode.Truncate" /> or <see langword="FileMode.Open" />, and the file specified by <paramref name="path" /> does not exist. The file must already exist in these modes.</exception> /// <exception cref="T:System.IO.IOException">An I/O error, such as specifying <see langword="FileMode.CreateNew" /> when the file specified by <paramref name="path" /> already exists, occurred. /// -or- /// The stream has been closed. /// -or- /// The disk was full (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file). /// -or- /// The file was too large (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).</exception> /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception> /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception> /// <exception cref="T:System.UnauthorizedAccessException">The <see cref="System.IO.FileStreamOptions.Access" /> requested is not permitted by the operating system for the specified <paramref name="path" />, such as when <see cref="System.IO.FileStreamOptions.Access" /> is <see cref="System.IO.FileAccess.Write" /> or <see cref="System.IO.FileAccess.ReadWrite" /> and the file or directory is set for read-only access. /// -or- /// <see cref="F:System.IO.FileOptions.Encrypted" /> is specified for <see cref="System.IO.FileStreamOptions.Options" /> , but file encryption is not supported on the current platform.</exception> /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. </exception> public FileStream(string path, FileStreamOptions options) { if (path is null) { throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); } else if (path.Length == 0) { throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); } else if (options is null) { throw new ArgumentNullException(nameof(options)); } else if ((options.Access & FileAccess.Read) != 0 && options.Mode == FileMode.Append) { throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(options)); } else if ((options.Access & FileAccess.Write) == 0) { if (options.Mode == FileMode.Truncate || options.Mode == FileMode.CreateNew || options.Mode == FileMode.Create || options.Mode == FileMode.Append) { throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, options.Mode, options.Access), nameof(options)); } } else if ((options.Access & FileAccess.Write) == FileAccess.Write) { SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } _strategy = FileStreamHelpers.ChooseStrategy( this, path, options.Mode, options.Access, options.Share, options.BufferSize, options.Options, options.PreallocationSize); }
internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset) { while (!buffer.IsEmpty) { fixed(byte *bufPtr = &MemoryMarshal.GetReference(buffer)) { // The Windows implementation uses WriteFile, which ignores the offset if the handle // isn't seekable. We do the same manually with PWrite vs Write, in order to enable // the function to be used by FileStream for all the same situations. int bytesWritten = handle.CanSeek ? Interop.Sys.PWrite(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length), fileOffset) : Interop.Sys.Write(handle, bufPtr, GetNumberOfBytesToWrite(buffer.Length)); FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path); if (bytesWritten == buffer.Length) { break; } // The write completed successfully but for fewer bytes than requested. // We need to try again for the remainder. buffer = buffer.Slice(bytesWritten); fileOffset += bytesWritten; } } }
private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); Debug.Assert(CanRead, "_parent.CanRead"); bool canSeek = CanSeek; if (canSeek) { VerifyOSHandlePosition(); } try { await FileStreamHelpers .AsyncModeCopyToAsync(_fileHandle, _path, canSeek, _filePosition, destination, bufferSize, cancellationToken) .ConfigureAwait(false); } finally { // Make sure the stream's current position reflects where we ended up if (!_fileHandle.IsClosed && CanSeek) { SeekCore(_fileHandle, 0, SeekOrigin.End); } } }
public sealed override void Flush() => Flush(flushToDisk: false); // we have nothing to flush as there is no buffer here internal sealed override void Flush(bool flushToDisk) { if (flushToDisk && CanWrite) { FileStreamHelpers.FlushToDisk(_fileHandle, _path); } }
/// <summary> /// Initializes a new instance of the <see cref="System.IO.FileStream" /> class with the specified path, creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size, additional file options and the allocation size. /// </summary> /// <param name="path">A relative or absolute path for the file that the current <see cref="System.IO.FileStream" /> instance will encapsulate.</param> /// <param name="options">An object that describes optional <see cref="System.IO.FileStream" /> parameters to use.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> or <paramref name="options" /> is <see langword="null" />.</exception> /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is an empty string (""), contains only white space, or contains one or more invalid characters. /// -or- /// <paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in an NTFS environment.</exception> /// <exception cref="T:System.NotSupportedException"><paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in a non-NTFS environment.</exception> /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found, such as when <see cref="System.IO.FileStreamOptions.Mode" /> is <see langword="FileMode.Truncate" /> or <see langword="FileMode.Open" />, and the file specified by <paramref name="path" /> does not exist. The file must already exist in these modes.</exception> /// <exception cref="T:System.IO.IOException">An I/O error, such as specifying <see langword="FileMode.CreateNew" /> when the file specified by <paramref name="path" /> already exists, occurred. /// -or- /// The stream has been closed. /// -or- /// The disk was full (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file). /// -or- /// The file was too large (when <see cref="System.IO.FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).</exception> /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception> /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception> /// <exception cref="T:System.UnauthorizedAccessException">The <see cref="System.IO.FileStreamOptions.Access" /> requested is not permitted by the operating system for the specified <paramref name="path" />, such as when <see cref="System.IO.FileStreamOptions.Access" /> is <see cref="System.IO.FileAccess.Write" /> or <see cref="System.IO.FileAccess.ReadWrite" /> and the file or directory is set for read-only access. /// -or- /// <see cref="F:System.IO.FileOptions.Encrypted" /> is specified for <see cref="System.IO.FileStreamOptions.Options" /> , but file encryption is not supported on the current platform.</exception> /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. </exception> public FileStream(string path, FileStreamOptions options) { ArgumentException.ThrowIfNullOrEmpty(path); ArgumentNullException.ThrowIfNull(options); if ((options.Access & FileAccess.Read) != 0 && options.Mode == FileMode.Append) { throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(options)); } else if ((options.Access & FileAccess.Write) == 0) { if (options.Mode == FileMode.Truncate || options.Mode == FileMode.CreateNew || options.Mode == FileMode.Create || options.Mode == FileMode.Append) { throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, options.Mode, options.Access), nameof(options)); } } if (options.PreallocationSize > 0) { FileStreamHelpers.ValidateArgumentsForPreallocation(options.Mode, options.Access); } if (options.UnixCreateMode.HasValue) { // Only allow UnixCreateMode for file modes that can create a new file. if (options.Mode == FileMode.Truncate || options.Mode == FileMode.Open) { throw new ArgumentException(SR.Argument_InvalidUnixCreateMode, nameof(options)); } } FileStreamHelpers.SerializationGuard(options.Access); _strategy = FileStreamHelpers.ChooseStrategy( this, path, options.Mode, options.Access, options.Share, options.BufferSize, options.Options, options.PreallocationSize, options.UnixCreateMode); }
internal LegacyFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) { string fullPath = Path.GetFullPath(path); _path = fullPath; _access = access; _bufferLength = bufferSize; if ((options & FileOptions.Asynchronous) != 0) { _useAsyncIO = true; } _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options); try { Init(mode, share, path, options); } catch { // If anything goes wrong while setting up the stream, make sure we deterministically dispose // of the opened handle. _fileHandle.Dispose(); _fileHandle = null !; throw; } }
internal static long GetFileLength(SafeFileHandle handle) { int result = Interop.Sys.FStat(handle, out Interop.Sys.FileStatus status); FileStreamHelpers.CheckFileCall(result, handle.Path); return(status.Size); }
private static unsafe int ReadSyncUsingAsyncHandle(SafeFileHandle handle, Span <byte> buffer, long fileOffset) { handle.EnsureThreadPoolBindingInitialized(); CallbackResetEvent resetEvent = new CallbackResetEvent(handle.ThreadPoolBinding !); NativeOverlapped * overlapped = null; try { overlapped = GetNativeOverlappedForAsyncHandle(handle, fileOffset, resetEvent); fixed(byte *pinned = &MemoryMarshal.GetReference(buffer)) { Interop.Kernel32.ReadFile(handle, pinned, buffer.Length, IntPtr.Zero, overlapped); int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); if (errorCode == Interop.Errors.ERROR_IO_PENDING) { resetEvent.WaitOne(); errorCode = Interop.Errors.ERROR_SUCCESS; } if (errorCode == Interop.Errors.ERROR_SUCCESS) { int result = 0; if (Interop.Kernel32.GetOverlappedResult(handle, overlapped, ref result, bWait: false)) { Debug.Assert(result >= 0 && result <= buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request"); return(result); } errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); } switch (errorCode) { case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) case Interop.Errors.ERROR_BROKEN_PIPE: // EOF on a pipe. Callback will not be called. // We clear the overlapped status bit for this special case (failure // to do so looks like we are freeing a pending overlapped later). overlapped->InternalLow = IntPtr.Zero; return(0); default: throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); } } } finally { if (overlapped != null) { resetEvent.FreeNativeOverlapped(overlapped); } resetEvent.Dispose(); } }
private static unsafe (SafeFileHandle.OverlappedValueTaskSource?vts, int errorCode) QueueAsyncReadFile(SafeFileHandle handle, Memory <byte> buffer, long fileOffset, CancellationToken cancellationToken, OSFileStreamStrategy?strategy) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); int errorCode = Interop.Errors.ERROR_SUCCESS; try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset, strategy); Debug.Assert(vts._memoryHandle.Pointer != null); // Queue an async ReadFile operation. if (Interop.Kernel32.ReadFile(handle, (byte *)vts._memoryHandle.Pointer, buffer.Length, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_IO_PENDING: // Common case: IO was initiated, completion will be handled by callback. // Register for cancellation now that the operation has been initiated. vts.RegisterForCancellation(cancellationToken); break; case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) case Interop.Errors.ERROR_BROKEN_PIPE: // EOF on a pipe. Callback will not be called. // We clear the overlapped status bit for this special case (failure // to do so looks like we are freeing a pending overlapped later). nativeOverlapped->InternalLow = IntPtr.Zero; vts.Dispose(); return(null, 0); default: // Error. Callback will not be called. vts.Dispose(); return(null, errorCode); } } } catch { vts.Dispose(); throw; } finally { if (errorCode != Interop.Errors.ERROR_IO_PENDING && errorCode != Interop.Errors.ERROR_SUCCESS) { strategy?.OnIncompleteOperation(buffer.Length, 0); } } // Completion handled by callback. vts.FinishedScheduling(); return(vts, -1); }
private static unsafe int ReadAtOffset(SafeFileHandle handle, Span <byte> buffer, long fileOffset) { fixed(byte *bufPtr = &MemoryMarshal.GetReference(buffer)) { int result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset); FileStreamHelpers.CheckFileCall(result, path: null); return(result); } }
internal static unsafe int WriteAtOffset(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset) { fixed(byte *bufPtr = &MemoryMarshal.GetReference(buffer)) { int result = Interop.Sys.PWrite(handle, bufPtr, buffer.Length, fileOffset); FileStreamHelpers.CheckFileCall(result, handle.Path); return(result); } }
private static unsafe (SafeFileHandle.OverlappedValueTaskSource?vts, int errorCode) QueueAsyncWriteFile(SafeFileHandle handle, ReadOnlyMemory <byte> buffer, long fileOffset, CancellationToken cancellationToken, OSFileStreamStrategy?strategy) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); int errorCode = Interop.Errors.ERROR_SUCCESS; try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(buffer, fileOffset, strategy); Debug.Assert(vts._memoryHandle.Pointer != null); // Queue an async WriteFile operation. if (Interop.Kernel32.WriteFile(handle, (byte *)vts._memoryHandle.Pointer, buffer.Length, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_IO_PENDING: // Common case: IO was initiated, completion will be handled by callback. // Register for cancellation now that the operation has been initiated. vts.RegisterForCancellation(cancellationToken); break; case Interop.Errors.ERROR_NO_DATA: // EOF on a pipe. IO callback will not be called. vts.Dispose(); return(null, 0); default: // Error. Callback will not be invoked. vts.Dispose(); return(null, errorCode); } } } catch { vts.Dispose(); throw; } finally { if (errorCode != Interop.Errors.ERROR_IO_PENDING && errorCode != Interop.Errors.ERROR_SUCCESS) { strategy?.OnIncompleteOperation(buffer.Length, 0); } } // Completion handled by callback. vts.FinishedScheduling(); return(vts, -1); }
// We absolutely need this method broken out so that WriteInternalCoreAsync can call // a method without having to go through buffering code that might call FlushWrite. protected unsafe void SetLengthCore(long value) { Debug.Assert(value >= 0, "value >= 0"); VerifyOSHandlePosition(); FileStreamHelpers.SetLength(_fileHandle, _path, value); if (_filePosition > value) { SeekCore(_fileHandle, 0, SeekOrigin.End); } }
private void InitFromHandleImpl(SafeFileHandle handle, out bool canSeek, out bool isPipe) { FileStreamHelpers.GetFileTypeSpecificInformation(handle, out canSeek, out isPipe); OnInitFromHandle(handle); if (_canSeek) { SeekCore(handle, 0, SeekOrigin.Current); } else { _filePosition = 0; } }
internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span <byte> buffer, long fileOffset) { fixed(byte *bufPtr = &MemoryMarshal.GetReference(buffer)) { // The Windows implementation uses ReadFile, which ignores the offset if the handle // isn't seekable. We do the same manually with PRead vs Read, in order to enable // the function to be used by FileStream for all the same situations. int result = handle.CanSeek ? Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset) : Interop.Sys.Read(handle, bufPtr, buffer.Length); FileStreamHelpers.CheckFileCall(result, handle.Path); return(result); } }
private static unsafe ValueTask <int> ReadFileScatterAsync(SafeFileHandle handle, MemoryHandle pinnedSegments, int bytesToRead, long fileOffset, CancellationToken cancellationToken) { handle.EnsureThreadPoolBindingInitialized(); SafeFileHandle.OverlappedValueTaskSource vts = handle.GetOverlappedValueTaskSource(); try { NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(Memory <byte> .Empty, fileOffset); Debug.Assert(pinnedSegments.Pointer != null); if (Interop.Kernel32.ReadFileScatter(handle, (long *)pinnedSegments.Pointer, bytesToRead, IntPtr.Zero, nativeOverlapped) == 0) { // The operation failed, or it's pending. int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); switch (errorCode) { case Interop.Errors.ERROR_IO_PENDING: // Common case: IO was initiated, completion will be handled by callback. // Register for cancellation now that the operation has been initiated. vts.RegisterForCancellation(cancellationToken); break; case Interop.Errors.ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) case Interop.Errors.ERROR_BROKEN_PIPE: // EOF on a pipe. Callback will not be called. // We clear the overlapped status bit for this special case (failure // to do so looks like we are freeing a pending overlapped later). nativeOverlapped->InternalLow = IntPtr.Zero; vts.Dispose(); return(ValueTask.FromResult(0)); default: // Error. Callback will not be called. vts.Dispose(); return(ValueTask.FromException <int>(Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path))); } } } catch { vts.Dispose(); throw; } // Completion handled by callback. vts.FinishedScheduling(); return(new ValueTask <int>(vts, vts.Version)); }
private void Init(FileMode mode, string originalPath) { FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, originalPath); OnInit(); // For Append mode... if (mode == FileMode.Append) { _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End); } else { _appendStart = -1; } }
internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset) { while (!buffer.IsEmpty) { fixed(byte *bufPtr = &MemoryMarshal.GetReference(buffer)) { // The Windows implementation uses WriteFile, which ignores the offset if the handle // isn't seekable. We do the same manually with PWrite vs Write, in order to enable // the function to be used by FileStream for all the same situations. int bytesToWrite = GetNumberOfBytesToWrite(buffer.Length); int bytesWritten; if (handle.SupportsRandomAccess) { bytesWritten = Interop.Sys.PWrite(handle, bufPtr, bytesToWrite, fileOffset); if (bytesWritten == -1) { // We need to fallback to the non-offset version for certain file types // e.g: character devices (such as /dev/tty), pipes, and sockets. Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.ENXIO || errorInfo.Error == Interop.Error.ESPIPE) { handle.SupportsRandomAccess = false; bytesWritten = Interop.Sys.Write(handle, bufPtr, bytesToWrite); } } } else { bytesWritten = Interop.Sys.Write(handle, bufPtr, bytesToWrite); } FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path); if (bytesWritten == buffer.Length) { break; } // The write completed successfully but for fewer bytes than requested. // We need to try again for the remainder. buffer = buffer.Slice(bytesWritten); fileOffset += bytesWritten; } } }
protected override void OnInitFromHandle(SafeFileHandle handle) { // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for // any particular file handle type. // If the handle was passed in without an explicit async setting, we already looked it up in GetDefaultIsAsync if (!handle.IsAsync.HasValue) { return; } // If we can't check the handle, just assume it is ok. if (!(FileStreamHelpers.IsHandleSynchronous(handle, ignoreInvalid: false) ?? true)) { throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle)); } }
public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) { SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle); try { ValidateHandle(safeHandle, access, bufferSize, isAsync); _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, DefaultShare, bufferSize, isAsync); } catch { // We don't want to take ownership of closing passed in handles // *unless* the constructor completes successfully. GC.SuppressFinalize(safeHandle); // This would also prevent Close from being called, but is unnecessary // as we've removed the object from the finalizer queue. // // safeHandle.SetHandleAsInvalid(); throw; } }
public StreamWriter(string path, bool append, Encoding encoding, int bufferSize) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } if (path.Length == 0) { throw new ArgumentException(SR.Argument_EmptyPath); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); } Stream stream = FileStreamHelpers.CreateFileStream(path, write: true, append: append); Init(stream, encoding, bufferSize, shouldLeaveOpen: false); }
private unsafe void WriteSpan(ReadOnlySpan <byte> source) { if (!CanWrite) { ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); // Make sure we are writing to the position that we think we are VerifyOSHandlePosition(); int r = FileStreamHelpers.WriteFileNative(_fileHandle, source, null, out int errorCode); if (r == -1) { // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. if (errorCode == ERROR_NO_DATA) { r = 0; } else { // ERROR_INVALID_PARAMETER may be returned for writes // where the position is too large or for synchronous writes // to a handle opened asynchronously. if (errorCode == ERROR_INVALID_PARAMETER) { throw new IOException(SR.IO_FileTooLongOrHandleNotSync); } throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); _filePosition += r; return; }
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush private unsafe int ReadSpan(Span <byte> destination) { if (!CanRead) { ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); // Make sure we are reading from the right spot VerifyOSHandlePosition(); int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination, null, out int errorCode); if (r == -1) { // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. if (errorCode == ERROR_BROKEN_PIPE) { r = 0; } else { if (errorCode == ERROR_INVALID_PARAMETER) { throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle"); } throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } } Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); _filePosition += r; return(r); }
internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { string fullPath = Path.GetFullPath(path); _path = fullPath; _access = access; _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options); try { _canSeek = true; Init(mode, path); } catch { // If anything goes wrong while setting up the stream, make sure we deterministically dispose // of the opened handle. _fileHandle.Dispose(); _fileHandle = null !; throw; } }