/// <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);
            }
        }
Exemple #7
0
        /// <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);
                }
            }
        }
Exemple #10
0
        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);
            }
        }
Exemple #11
0
        /// <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;
            }
        }
Exemple #13
0
        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);
        }
Exemple #16
0
        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);
            }
        }
Exemple #17
0
        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);
        }
Exemple #19
0
        // 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);
            }
        }
Exemple #20
0
        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;
            }
        }
Exemple #21
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);
            }
        }
Exemple #22
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));
        }
Exemple #23
0
        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;
            }
        }
Exemple #24
0
        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));
            }
        }
Exemple #26
0
        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;
            }
        }
Exemple #27
0
        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);
        }
Exemple #30
0
        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;
            }
        }