Example #1
0
        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 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.
                case Interop.Errors.ERROR_INVALID_PARAMETER when IsEndOfFileForNoBuffering(handle, fileOffset):
                    return(0);

                default:
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path);
                }
            }
        }
Example #3
0
        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.ThreadPoolBinding !, 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();
            }
        }
Example #4
0
        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);
        }
Example #5
0
        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);
        }
Example #6
0
        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));
        }
Example #7
0
        private static unsafe void WriteSyncUsingAsyncHandle(SafeFileHandle handle, ReadOnlySpan <byte> buffer, long fileOffset)
        {
            if (buffer.IsEmpty)
            {
                return;
            }

            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.WriteFile(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 == buffer.Length, $"GetOverlappedResult returned {result} for {buffer.Length} bytes request");
                            return;
                        }

                        errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(handle);
                    }

                    switch (errorCode)
                    {
                    case Interop.Errors.ERROR_NO_DATA:
                        // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
                        return;

                    case Interop.Errors.ERROR_INVALID_PARAMETER:
                        // ERROR_INVALID_PARAMETER may be returned for writes
                        // where the position is too large or for synchronous writes
                        // to a handle opened asynchronously.
                        throw new IOException(SR.IO_FileTooLong);

                    default:
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path);
                    }
                }
            }
            finally
            {
                if (overlapped != null)
                {
                    resetEvent.FreeNativeOverlapped(overlapped);
                }

                resetEvent.Dispose();
            }
        }
        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);
                    }
                    else
                    {
                        // The initial errorCode was neither ERROR_IO_PENDING nor ERROR_SUCCESS, so the operation
                        // failed with an error and the callback won't be invoked.  We thus need to decrement the
                        // ref count on the resetEvent that was initialized to a value under the expectation that
                        // the callback would be invoked and decrement it.
                        resetEvent.ReleaseRefCount(overlapped);
                    }

                    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:
                    case Interop.Errors.ERROR_INVALID_PARAMETER when IsEndOfFileForNoBuffering(handle, fileOffset):
                        // 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.ReleaseRefCount(overlapped);
                }

                resetEvent.Dispose();
            }
        }