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");

            NativeOverlapped nativeOverlapped = GetNativeOverlappedForCurrentPosition();
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination, true, &nativeOverlapped, out int errorCode);

            if (r == -1)
            {
                // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
                if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE)
                {
                    r = 0;
                }
                else
                {
                    if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER)
                    {
                        ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle));
                    }

                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                }
            }
            Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken.");
            _filePosition += r;

            return(r);
        }
        private unsafe void WriteSpan(ReadOnlySpan <byte> source)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            NativeOverlapped nativeOverlapped = GetNativeOverlappedForCurrentPosition();
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source, true, &nativeOverlapped, out int errorCode);

            if (r == -1)
            {
                // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
                if (errorCode == Interop.Errors.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 == Interop.Errors.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;
            UpdateLengthOnChangePosition();
        }
Exemple #3
0
        internal LegacyFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
        {
            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;
            }
        }
        protected sealed override void Dispose(bool disposing)
        {
            if (_strategy.IsClosed)
            {
                return;
            }

            try
            {
                Flush();
            }
            catch (Exception e) when(!disposing && FileStreamHelpers.IsIoRelatedException(e))
            {
                // On finalization, ignore failures from trying to flush the write buffer,
                // e.g. if this stream is wrapping a pipe and the pipe is now broken.
            }
            finally
            {
                // Don't set the buffer to null, to avoid a NullReferenceException
                // when users have a race condition in their code (i.e. they call
                // FileStream.Close when calling another method on FileStream like Read).

                // There is no need to call base.Dispose as it's empty
                _writePos = 0;

                _strategy.DisposeInternal(disposing);
            }
        }
Exemple #5
0
        internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
        {
            string fullPath = Path.GetFullPath(path);

            _path   = fullPath;
            _access = access;
            _share  = share;

            _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;
            }
        }
Exemple #6
0
        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 #7
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 #8
0
        internal override void Flush(bool flushToDisk)
        {
            FlushInternalBuffer();

            if (flushToDisk && CanWrite)
            {
                FileStreamHelpers.FlushToDisk(_fileHandle);
            }
        }
Exemple #9
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.SetFileLength(_fileHandle, _path, value);

            if (_filePosition > value)
            {
                SeekCore(_fileHandle, 0, SeekOrigin.End);
            }
        }
        /// <summary>Releases the unmanaged resources used by the stream.</summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (_fileHandle != null && !_fileHandle.IsClosed)
                {
                    // Flush any remaining data in the file
                    try
                    {
                        FlushWriteBuffer();
                    }
                    catch (Exception e) when(!disposing && FileStreamHelpers.IsIoRelatedException(e))
                    {
                        // On finalization, ignore failures from trying to flush the write buffer,
                        // e.g. if this stream is wrapping a pipe and the pipe is now broken.
                    }

                    // If DeleteOnClose was requested when constructed, delete the file now.
                    // (Unix doesn't directly support DeleteOnClose, so we mimic it here.)
                    if (_path != null && (_options & FileOptions.DeleteOnClose) != 0)
                    {
                        // Since we still have the file open, this will end up deleting
                        // it (assuming we're the only link to it) once it's closed, but the
                        // name will be removed immediately.
                        Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist
                    }

                    // Closing the file handle can fail, e.g. due to out of disk space
                    // Throw these errors as exceptions when disposing
                    if (_fileHandle != null && !_fileHandle.IsClosed && disposing)
                    {
                        SafeFileHandle.t_lastCloseErrorInfo = null;

                        _fileHandle.Dispose();

                        if (SafeFileHandle.t_lastCloseErrorInfo != null)
                        {
                            throw Interop.GetExceptionForIoErrno(SafeFileHandle.t_lastCloseErrorInfo.GetValueOrDefault(), _path, isDirectory: false);
                        }
                    }
                }
            }
            finally
            {
                if (_fileHandle != null && !_fileHandle.IsClosed)
                {
                    _fileHandle.Dispose();
                }
                base.Dispose(disposing);
            }
        }
Exemple #11
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 #12
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;
            }
        }
        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))
            {
                ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle));
            }
        }
Exemple #14
0
        private long _appendStart; // When appending, prevent overwriting file.

        internal OSFileStreamStrategy(SafeFileHandle handle, FileAccess access)
        {
            _access = access;

            handle.EnsureThreadPoolBindingInitialized();

            if (handle.CanSeek)
            {
                // given strategy was created out of existing handle, so we have to perform
                // a syscall to get the current handle offset
                _filePosition = FileStreamHelpers.Seek(handle, 0, SeekOrigin.Current);
            }
            else
            {
                _filePosition = 0;
            }

            _fileHandle = handle;
        }
Exemple #15
0
        private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
        {
            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
            Debug.Assert(CanRead, "_parent.CanRead");

            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)
                {
                    _filePosition = Length;
                }
            }
        }
Exemple #16
0
        /// <summary>Releases the unmanaged resources used by the stream.</summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (_fileHandle != null && !_fileHandle.IsClosed)
                {
                    // Flush any remaining data in the file
                    try
                    {
                        FlushWriteBuffer();
                    }
                    catch (Exception e) when(!disposing && FileStreamHelpers.IsIoRelatedException(e))
                    {
                        // On finalization, ignore failures from trying to flush the write buffer,
                        // e.g. if this stream is wrapping a pipe and the pipe is now broken.
                    }

                    // Closing the file handle can fail, e.g. due to out of disk space
                    // Throw these errors as exceptions when disposing
                    if (_fileHandle != null && !_fileHandle.IsClosed && disposing)
                    {
                        SafeFileHandle.t_lastCloseErrorInfo = null;

                        _fileHandle.Dispose();

                        if (SafeFileHandle.t_lastCloseErrorInfo != null)
                        {
                            throw Interop.GetExceptionForIoErrno(SafeFileHandle.t_lastCloseErrorInfo.GetValueOrDefault(), _fileHandle.Path, isDirectory: false);
                        }
                    }
                }
            }
            finally
            {
                if (_fileHandle != null && !_fileHandle.IsClosed)
                {
                    _fileHandle.Dispose();
                }
                base.Dispose(disposing);
            }
        }
        private bool _exposedHandle; // created from handle, or SafeFileHandle was used and the handle got exposed

        internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share)
        {
            _access        = access;
            _share         = share;
            _exposedHandle = true;

            handle.InitThreadPoolBindingIfNeeded();

            if (handle.CanSeek)
            {
                // given strategy was created out of existing handle, so we have to perform
                // a syscall to get the current handle offset
                _filePosition = FileStreamHelpers.Seek(handle, _path, 0, SeekOrigin.Current);
            }
            else
            {
                _filePosition = 0;
            }

            _fileHandle = handle;
        }
        private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // valueTaskSource is not null when:
            // - First time calling WriteAsync in buffered mode
            // - Second+ time calling WriteAsync, both buffered or unbuffered
            // - On buffered flush, when source memory is also the internal buffer
            // valueTaskSource is null when:
            // - First time calling WriteAsync in unbuffered mode
            ValueTaskSource   valueTaskSource = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);
            NativeOverlapped *intOverlapped   = valueTaskSource.Configure(source);

            long positionBefore = _filePosition;

            if (CanSeek)
            {
                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = (int)positionBefore;
                intOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves, but only in memory.  This isn't threadsafe.
                _filePosition += source.Length;
                UpdateLengthOnChangePosition();
            }

            // queue an async WriteFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, false, intOverlapped, out int errorCode);

            // WriteFile, the OS version, will return 0 on failure.  But
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using
            // overlapped structures!  This is ByDesign NT behavior.
            if (r == -1)
            {
                // For pipes, when they are closed on the other side, they will come here.
                if (errorCode == Interop.Errors.ERROR_NO_DATA)
                {
                    // Not an error, but EOF. AsyncFSCallback will NOT be called.
                    // Completing TCS and return cached task allowing the GC to collect TCS.
                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);
                    return(ValueTask.CompletedTask);
                }
                else if (errorCode != Interop.Errors.ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        _filePosition = positionBefore;
                    }

                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);

                    if (errorCode == Interop.Errors.ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    valueTaskSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from WriteFileNative
                // when we do async IO instead of the number of bytes written,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(new ValueTask(valueTaskSource, valueTaskSource.Version));
        }
        private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // valueTaskSource is not null when:
            // - First time calling ReadAsync in buffered mode
            // - Second+ time calling ReadAsync, both buffered or unbuffered
            // - On buffered flush, when source memory is also the internal buffer
            // valueTaskSource is null when:
            // - First time calling ReadAsync in unbuffered mode
            ValueTaskSource   valueTaskSource = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);
            NativeOverlapped *intOverlapped   = valueTaskSource.Configure(destination);

            // Calculate position in the file we should be at after the read is done
            long positionBefore = _filePosition;

            if (CanSeek)
            {
                long len = Length;

                if (positionBefore + destination.Length > len)
                {
                    if (positionBefore <= len)
                    {
                        destination = destination.Slice(0, (int)(len - positionBefore));
                    }
                    else
                    {
                        destination = default;
                    }
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = unchecked ((int)positionBefore);
                intOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves, but only in memory. This isn't threadsafe.
                _filePosition += destination.Length;
            }

            // queue an async ReadFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, false, intOverlapped, out int errorCode);

            // ReadFile, the OS version, will return 0 on failure.  But
            // my ReadFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures!  This is by design NT behavior.
            if (r == -1)
            {
                // For pipes, when they hit EOF, they will come here.
                if (errorCode == Interop.Errors.ERROR_BROKEN_PIPE)
                {
                    // Not an error, but EOF.  AsyncFSCallback will NOT be
                    // called.  Call the user callback here.

                    // We clear the overlapped status bit for this special case.
                    // Failure to do so looks like we are freeing a pending overlapped later.
                    intOverlapped->InternalLow = IntPtr.Zero;
                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);
                    return(new ValueTask <int>(0));
                }
                else if (errorCode != Interop.Errors.ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        _filePosition = positionBefore;
                    }

                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);

                    if (errorCode == Interop.Errors.ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    valueTaskSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from ReadFileNative
                // when we do async IO instead of the number of bytes read,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(new ValueTask <int>(valueTaskSource, valueTaskSource.Version));
        }
Exemple #20
0
        // This doesn't do argument checking.  Necessary for SetLength, which must
        // set the file pointer beyond the end of the file. This will update the
        // internal position
        private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false)
        {
            Debug.Assert(fileHandle.CanSeek, "fileHandle.CanSeek");

            return(_filePosition = FileStreamHelpers.Seek(fileHandle, offset, origin, closeInvalidHandle));
        }
Exemple #21
0
 internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, _path, position, length);
Exemple #22
0
        private unsafe Task <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, destination);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            // Calculate position in the file we should be at after the read is done
            if (CanSeek)
            {
                long len = Length;

                // Make sure we are reading from the position that we think we are
                VerifyOSHandlePosition();

                if (destination.Length > len - _filePosition)
                {
                    if (_filePosition <= len)
                    {
                        destination = destination.Slice(0, (int)(len - _filePosition));
                    }
                    else
                    {
                        destination = default;
                    }
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = unchecked ((int)_filePosition);
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves. This isn't threadsafe.

                // WriteFile should not update the file pointer when writing
                // in overlapped mode, according to MSDN.  But it does update
                // the file pointer when writing to a UNC path!
                // So changed the code below to seek to an absolute
                // location, not a relative one.  ReadFile seems consistent though.
                SeekCore(_fileHandle, destination.Length, SeekOrigin.Current);
            }

            // queue an async ReadFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, intOverlapped, out int errorCode);

            // ReadFile, the OS version, will return 0 on failure.  But
            // my ReadFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures!  This is by design NT behavior.
            if (r == -1)
            {
                // For pipes, when they hit EOF, they will come here.
                if (errorCode == ERROR_BROKEN_PIPE)
                {
                    // Not an error, but EOF.  AsyncFSCallback will NOT be
                    // called.  Call the user callback here.

                    // We clear the overlapped status bit for this special case.
                    // Failure to do so looks like we are freeing a pending overlapped later.
                    intOverlapped->InternalLow = IntPtr.Zero;
                    completionSource.SetCompletedSynchronously(0);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from ReadFileNative
                // when we do async IO instead of the number of bytes read,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }
Exemple #23
0
        private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, source);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            if (CanSeek)
            {
                // Make sure we set the length of the file appropriately.
                long len = Length;

                // Make sure we are writing to the position that we think we are
                VerifyOSHandlePosition();

                if (_filePosition + source.Length > len)
                {
                    SetLengthCore(_filePosition + source.Length);
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = (int)_filePosition;
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves.  This isn't threadsafe.
                SeekCore(_fileHandle, source.Length, SeekOrigin.Current);
            }

            // queue an async WriteFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, intOverlapped, out int errorCode);

            // WriteFile, the OS version, will return 0 on failure.  But
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using
            // overlapped structures!  This is ByDesign NT behavior.
            if (r == -1)
            {
                // For pipes, when they are closed on the other side, they will come here.
                if (errorCode == ERROR_NO_DATA)
                {
                    // Not an error, but EOF. AsyncFSCallback will NOT be called.
                    // Completing TCS and return cached task allowing the GC to collect TCS.
                    completionSource.SetCompletedSynchronously(0);
                    return(Task.CompletedTask);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from WriteFileNative
                // when we do async IO instead of the number of bytes written,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }
Exemple #24
0
 /// <summary>Allows access by other processes to all or part of a file that was previously locked.</summary>
 /// <param name="position">The beginning of the range to unlock.</param>
 /// <param name="length">The range to be unlocked.</param>
 internal override void Unlock(long position, long length) =>
 FileStreamHelpers.Unlock(_fileHandle, position, length);
Exemple #25
0
 /// <summary>Prevents other processes from reading from or writing to the FileStream.</summary>
 /// <param name="position">The beginning of the range to lock.</param>
 /// <param name="length">The range to be locked.</param>
 internal override void Lock(long position, long length) =>
 FileStreamHelpers.Lock(_fileHandle, CanWrite, position, length);
Exemple #26
0
        private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(source);
                Debug.Assert(vts._memoryHandle.Pointer != null);

                long positionBefore = _filePosition;
                if (CanSeek)
                {
                    // Now set the position to read from in the NativeOverlapped struct
                    // For pipes, we should leave the offset fields set to 0.
                    nativeOverlapped->OffsetLow  = (int)positionBefore;
                    nativeOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory.  This isn't threadsafe.
                    _filePosition += source.Length;
                    UpdateLengthOnChangePosition();
                }

                // Queue an async WriteFile operation.
                if (Interop.Kernel32.WriteFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, source.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    if (errorCode == 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);
                    }
                    else
                    {
                        // Error. Callback will not be invoked.
                        vts.Dispose();
                        return(errorCode == Interop.Errors.ERROR_NO_DATA ? // EOF on a pipe. IO callback will not be called.
                               ValueTask.CompletedTask :
                               ValueTask.FromException(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask(vts, vts.Version));
        }
Exemple #27
0
        private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(destination);
                Debug.Assert(vts._memoryHandle.Pointer != null);

                // Calculate position in the file we should be at after the read is done
                long positionBefore = _filePosition;
                if (CanSeek)
                {
                    long len = Length;

                    if (positionBefore + destination.Length > len)
                    {
                        destination = positionBefore <= len?
                                      destination.Slice(0, (int)(len - positionBefore)) :
                                          default;
                    }

                    // Now set the position to read from in the NativeOverlapped struct
                    // For pipes, we should leave the offset fields set to 0.
                    nativeOverlapped->OffsetLow  = unchecked ((int)positionBefore);
                    nativeOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory. This isn't threadsafe.
                    _filePosition += destination.Length;
                }

                // Queue an async ReadFile operation.
                if (Interop.Kernel32.ReadFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, destination.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    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_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>(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask <int>(vts, vts.Version));
        }
Exemple #28
0
        /// <summary>Initializes a stream for reading or writing a Unix file.</summary>
        /// <param name="mode">How the file should be opened.</param>
        /// <param name="share">What other access to the file should be allowed.  This is currently ignored.</param>
        /// <param name="originalPath">The original path specified for the FileStream.</param>
        /// <param name="options">Options, passed via arguments as we have no guarantee that _options field was already set.</param>
        /// <param name="preallocationSize">passed to posix_fallocate</param>
        private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options, long preallocationSize)
        {
            // FileStream performs most of the general argument validation.  We can assume here that the arguments
            // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.)
            // Store the arguments
            _mode    = mode;
            _options = options;

            if (_useAsyncIO)
            {
                _asyncState = new AsyncState();
            }

            _fileHandle.IsAsync = _useAsyncIO;

            // Lock the file if requested via FileShare.  This is only advisory locking. FileShare.None implies an exclusive
            // lock on the file and all other modes use a shared lock.  While this is not as granular as Windows, not mandatory,
            // and not atomic with file opening, it's better than nothing.
            Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH;
            if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0)
            {
                // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone
                // else and we would block trying to access it.  Other errors, such as ENOTSUP (locking isn't supported) or
                // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value,
                // given again that this is only advisory / best-effort.
                Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                if (errorInfo.Error == Interop.Error.EWOULDBLOCK)
                {
                    throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
                }
            }

            // These provide hints around how the file will be accessed.  Specifying both RandomAccess
            // and Sequential together doesn't make sense as they are two competing options on the same spectrum,
            // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided).
            Interop.Sys.FileAdvice fadv =
                (options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM :
                (options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL :
                0;
            if (fadv != 0)
            {
                CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv),
                              ignoreNotSupported: true); // just a hint.
            }

            if (mode == FileMode.Append)
            {
                // Jump to the end of the file if opened as Append.
                _appendStart = SeekCore(_fileHandle, 0, SeekOrigin.End);
            }
            else if (mode == FileMode.Create || mode == FileMode.Truncate)
            {
                // Truncate the file now if the file mode requires it. This ensures that the file only will be truncated
                // if opened successfully.
                if (Interop.Sys.FTruncate(_fileHandle, 0) < 0)
                {
                    Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                    if (errorInfo.Error != Interop.Error.EBADF && errorInfo.Error != Interop.Error.EINVAL)
                    {
                        // We know the file descriptor is valid and we know the size argument to FTruncate is correct,
                        // so if EBADF or EINVAL is returned, it means we're dealing with a special file that can't be
                        // truncated.  Ignore the error in such cases; in all others, throw.
                        throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
                    }
                }
            }

            // If preallocationSize has been provided for a creatable and writeable file
            if (FileStreamHelpers.ShouldPreallocate(preallocationSize, _access, mode))
            {
                int fallocateResult = Interop.Sys.PosixFAllocate(_fileHandle, 0, preallocationSize);
                if (fallocateResult != 0)
                {
                    _fileHandle.Dispose();
                    Interop.Sys.Unlink(_path !); // remove the file to mimic Windows behaviour (atomic operation)

                    if (fallocateResult == -1)
                    {
                        throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, _path, preallocationSize));
                    }

                    Debug.Assert(fallocateResult == -2);
                    throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, _path, preallocationSize));
                }
            }
        }
Exemple #29
0
        // This doesn't do argument checking.  Necessary for SetLength, which must
        // set the file pointer beyond the end of the file. This will update the
        // internal position
        protected long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false)
        {
            Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek");

            return(_filePosition = FileStreamHelpers.Seek(fileHandle, _path, offset, origin, closeInvalidHandle));
        }