diff options
Diffstat (limited to 'src/mscorlib/src/System/IO')
36 files changed, 854 insertions, 10590 deletions
diff --git a/src/mscorlib/src/System/IO/BinaryReader.cs b/src/mscorlib/src/System/IO/BinaryReader.cs index 8accf0bd77..4145a7f4f6 100644 --- a/src/mscorlib/src/System/IO/BinaryReader.cs +++ b/src/mscorlib/src/System/IO/BinaryReader.cs @@ -19,6 +19,7 @@ namespace System.IO { using System.Runtime; using System.Text; using System.Globalization; + using System.Diagnostics; using System.Diagnostics.Contracts; using System.Security; @@ -40,7 +41,7 @@ namespace System.IO { private bool m_isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf private bool m_leaveOpen; - public BinaryReader(Stream input) : this(input, new UTF8Encoding(), false) { + public BinaryReader(Stream input) : this(input, Encoding.UTF8, false) { } public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) { @@ -48,10 +49,10 @@ namespace System.IO { public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) { if (input==null) { - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); } if (encoding==null) { - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); } if (!input.CanRead) throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable")); @@ -73,7 +74,7 @@ namespace System.IO { m_isMemoryStream = (m_stream.GetType() == typeof(MemoryStream)); m_leaveOpen = leaveOpen; - Contract.Assert(m_decoder!=null, "[BinaryReader.ctor]m_decoder!=null"); + Debug.Assert(m_decoder!=null, "[BinaryReader.ctor]m_decoder!=null"); } public virtual Stream BaseStream { @@ -173,7 +174,7 @@ namespace System.IO { if (m_stream==null) __Error.FileNotOpen(); // read directly from MemoryStream buffer MemoryStream mStream = m_stream as MemoryStream; - Contract.Assert(mStream != null, "m_stream as MemoryStream != null"); + Debug.Assert(mStream != null, "m_stream as MemoryStream != null"); return mStream.InternalReadInt32(); } @@ -209,14 +210,12 @@ namespace System.IO { return ((ulong)hi) << 32 | lo; } - [System.Security.SecuritySafeCritical] // auto-generated public virtual unsafe float ReadSingle() { FillBuffer(4); uint tmpBuffer = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24); return *((float*)&tmpBuffer); } - [System.Security.SecuritySafeCritical] // auto-generated public virtual unsafe double ReadDouble() { FillBuffer(8); uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | @@ -294,16 +293,15 @@ namespace System.IO { return StringBuilderCache.GetStringAndRelease(sb); } - [SecuritySafeCritical] public virtual int Read(char[] buffer, int index, int count) { if (buffer==null) { - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); } if (index < 0) { - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (count < 0) { - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (buffer.Length - index < count) { throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); @@ -319,11 +317,10 @@ namespace System.IO { return InternalReadChars(buffer, index, count); } - [SecurityCritical] private int InternalReadChars(char[] buffer, int index, int count) { Contract.Requires(buffer != null); Contract.Requires(index >= 0 && count >= 0); - Contract.Assert(m_stream != null); + Debug.Assert(m_stream != null); int numBytes = 0; int charsRemaining = count; @@ -355,7 +352,7 @@ namespace System.IO { if (m_isMemoryStream) { MemoryStream mStream = m_stream as MemoryStream; - Contract.Assert(mStream != null, "m_stream as MemoryStream != null"); + Debug.Assert(mStream != null, "m_stream as MemoryStream != null"); position = mStream.InternalGetPosition(); numBytes = mStream.InternalEmulateRead(numBytes); @@ -371,7 +368,7 @@ namespace System.IO { return (count - charsRemaining); } - Contract.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); + Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); checked { @@ -398,7 +395,7 @@ namespace System.IO { } // this should never fail - Contract.Assert(charsRemaining >= 0, "We read too many characters."); + Debug.Assert(charsRemaining >= 0, "We read too many characters."); // we may have read fewer than the number of characters requested if end of stream reached // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence) @@ -447,7 +444,7 @@ namespace System.IO { return -1; } - Contract.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only."); + Debug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only."); try { @@ -464,7 +461,7 @@ namespace System.IO { throw; } - Contract.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!"); + Debug.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!"); // Console.WriteLine("That became: " + charsRead + " characters."); } if (charsRead == 0) @@ -472,10 +469,9 @@ namespace System.IO { return m_singleChar[0]; } - [SecuritySafeCritical] public virtual char[] ReadChars(int count) { if (count<0) { - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } Contract.Ensures(Contract.Result<char[]>() != null); Contract.Ensures(Contract.Result<char[]>().Length <= count); @@ -502,11 +498,11 @@ namespace System.IO { public virtual int Read(byte[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.Ensures(Contract.Result<int>() >= 0); @@ -518,7 +514,7 @@ namespace System.IO { } public virtual byte[] ReadBytes(int count) { - if (count < 0) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); Contract.Ensures(Contract.Result<byte[]>() != null); Contract.Ensures(Contract.Result<byte[]>().Length <= Contract.OldValue(count)); Contract.EndContractBlock(); @@ -551,7 +547,7 @@ namespace System.IO { protected virtual void FillBuffer(int numBytes) { if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) { - throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_BinaryReaderFillBuffer")); + throw new ArgumentOutOfRangeException(nameof(numBytes), Environment.GetResourceString("ArgumentOutOfRange_BinaryReaderFillBuffer")); } int bytesRead=0; int n = 0; diff --git a/src/mscorlib/src/System/IO/BinaryWriter.cs b/src/mscorlib/src/System/IO/BinaryWriter.cs index c775cbc9ff..f99b4d3d42 100644 --- a/src/mscorlib/src/System/IO/BinaryWriter.cs +++ b/src/mscorlib/src/System/IO/BinaryWriter.cs @@ -17,6 +17,7 @@ using System; using System.Runtime; using System.Runtime.Serialization; using System.Text; +using System.Diagnostics; using System.Diagnostics.Contracts; namespace System.IO { @@ -25,7 +26,7 @@ namespace System.IO { // give unique encodings. // [Serializable] -[System.Runtime.InteropServices.ComVisible(true)] + [System.Runtime.InteropServices.ComVisible(true)] public class BinaryWriter : IDisposable { public static readonly BinaryWriter Null = new BinaryWriter(); @@ -38,14 +39,6 @@ namespace System.IO { [OptionalField] // New in .NET FX 4.5. False is the right default value. private bool _leaveOpen; - // This field should never have been serialized and has not been used since before v2.0. - // However, this type is serializable, and we need to keep the field name around when deserializing. - // Also, we'll make .NET FX 4.5 not break if it's missing. -#pragma warning disable 169 - [OptionalField] - private char[] _tmpOneCharBuffer; -#pragma warning restore 169 - // Perf optimization stuff private byte[] _largeByteBuffer; // temp space for writing chars. private int _maxChars; // max # of chars we can put in _largeByteBuffer @@ -58,11 +51,11 @@ namespace System.IO { { OutStream = Stream.Null; _buffer = new byte[16]; - _encoding = new UTF8Encoding(false, true); + _encoding = EncodingCache.UTF8NoBOM; _encoder = _encoding.GetEncoder(); } - public BinaryWriter(Stream output) : this(output, new UTF8Encoding(false, true), false) + public BinaryWriter(Stream output) : this(output, EncodingCache.UTF8NoBOM, false) { } @@ -73,9 +66,9 @@ namespace System.IO { public BinaryWriter(Stream output, Encoding encoding, bool leaveOpen) { if (output==null) - throw new ArgumentNullException("output"); + throw new ArgumentNullException(nameof(output)); if (encoding==null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (!output.CanWrite) throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable")); Contract.EndContractBlock(); @@ -166,7 +159,7 @@ namespace System.IO { // public virtual void Write(byte[] buffer) { if (buffer == null) - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); Contract.EndContractBlock(); OutStream.Write(buffer, 0, buffer.Length); } @@ -185,13 +178,12 @@ namespace System.IO { // advanced by two. // Note this method cannot handle surrogates properly in UTF-8. // - [System.Security.SecuritySafeCritical] // auto-generated public unsafe virtual void Write(char ch) { if (Char.IsSurrogate(ch)) throw new ArgumentException(Environment.GetResourceString("Arg_SurrogatesNotAllowedAsSingleChar")); Contract.EndContractBlock(); - Contract.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); + Debug.Assert(_encoding.GetMaxByteCount(1) <= 16, "_encoding.GetMaxByteCount(1) <= 16)"); int numBytes = 0; fixed(byte * pBytes = _buffer) { numBytes = _encoder.GetBytes(&ch, 1, pBytes, _buffer.Length, flush: true); @@ -207,7 +199,7 @@ namespace System.IO { public virtual void Write(char[] chars) { if (chars == null) - throw new ArgumentNullException("chars"); + throw new ArgumentNullException(nameof(chars)); Contract.EndContractBlock(); byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); @@ -229,7 +221,6 @@ namespace System.IO { // Writes a double to this stream. The current position of the stream is // advanced by eight. // - [System.Security.SecuritySafeCritical] // auto-generated public unsafe virtual void Write(double value) { ulong TmpValue = *(ulong *)&value; @@ -332,7 +323,6 @@ namespace System.IO { // Writes a float to this stream. The current position of the stream is // advanced by four. // - [System.Security.SecuritySafeCritical] // auto-generated public unsafe virtual void Write(float value) { uint TmpValue = *(uint *)&value; @@ -349,11 +339,10 @@ namespace System.IO { // a four-byte unsigned integer, and then writes that many characters // to the stream. // - [System.Security.SecuritySafeCritical] // auto-generated public unsafe virtual void Write(String value) { if (value==null) - throw new ArgumentNullException("value"); + throw new ArgumentNullException(nameof(value)); Contract.EndContractBlock(); int len = _encoding.GetByteCount(value); @@ -366,7 +355,7 @@ namespace System.IO { if (len <= _largeByteBuffer.Length) { - //Contract.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); + //Debug.Assert(len == _encoding.GetBytes(chars, 0, chars.Length, _largeByteBuffer, 0), "encoding's GetByteCount & GetBytes gave different answers! encoding type: "+_encoding.GetType().Name); _encoding.GetBytes(value, 0, value.Length, _largeByteBuffer, 0); OutStream.Write(_largeByteBuffer, 0, len); } @@ -401,14 +390,14 @@ namespace System.IO { } #if _DEBUG totalBytes += byteLen; - Contract.Assert (totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); + Debug.Assert (totalBytes <= len && byteLen <= _largeByteBuffer.Length, "BinaryWriter::Write(String) - More bytes encoded than expected!"); #endif OutStream.Write(_largeByteBuffer, 0, byteLen); charStart += charCount; numLeft -= charCount; } #if _DEBUG - Contract.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!"); + Debug.Assert(totalBytes == len, "BinaryWriter::Write(String) - Didn't write out all the bytes!"); #endif } } diff --git a/src/mscorlib/src/System/IO/BufferedStream.cs b/src/mscorlib/src/System/IO/BufferedStream.cs deleted file mode 100644 index 0c73b5c0f5..0000000000 --- a/src/mscorlib/src/System/IO/BufferedStream.cs +++ /dev/null @@ -1,1320 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** Purpose: A composable Stream that buffers reads & writes to the underlying stream. -** -** -===========================================================*/ -using System; -using System.Runtime.InteropServices; -using System.Globalization; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Collections.ObjectModel; -using System.Security; -using System.Threading.Tasks; - -namespace System.IO { - -/// <summary> -/// One of the design goals here is to prevent the buffer from getting in the way and slowing -/// down underlying stream accesses when it is not needed. If you always read & write for sizes -/// greater than the internal buffer size, then this class may not even allocate the internal buffer. -/// See a large comment in Write for the details of the write buffer heuristic. -/// -/// This class buffers reads & writes in a shared buffer. -/// (If you maintained two buffers separately, one operation would always trash the other buffer -/// anyways, so we might as well use one buffer.) -/// The assumption here is you will almost always be doing a series of reads or writes, but rarely -/// alternate between the two of them on the same stream. -/// -/// Class Invariants: -/// The class has one buffer, shared for reading & writing. -/// It can only be used for one or the other at any point in time - not both. -/// The following should be true: -/// <![CDATA[ -/// * 0 <= _readPos <= _readLen < _bufferSize -/// * 0 <= _writePos < _bufferSize -/// * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, but we're at the end of the buffer. -/// * _readPos == _readLen == 0 means the read buffer contains garbage. -/// * Either _writePos can be greater than 0, or _readLen & _readPos can be greater than zero, -/// but neither can be greater than zero at the same time. -/// ]]> -/// This class will never cache more bytes than the max specified buffer size. -/// However, it may use a temporary buffer of up to twice the size in order to combine several IO operations on -/// the underlying stream into a single operation. This is because we assume that memory copies are significantly -/// faster than IO operations on the underlying stream (if this was not true, using buffering is never appropriate). -/// The max size of this "shadow" buffer is limited as to not allocate it on the LOH. -/// Shadowing is always transient. Even when using this technique, this class still guarantees that the number of -/// bytes cached (not yet written to the target stream or not yet consumed by the user) is never larger than the -/// actual specified buffer size. -/// </summary> -[ComVisible(true)] -public sealed class BufferedStream : Stream { - - - private const Int32 _DefaultBufferSize = 4096; - - - private Stream _stream; // Underlying stream. Close sets _stream to null. - - private Byte[] _buffer; // Shared read/write buffer. Alloc on first use. - - private readonly Int32 _bufferSize; // Length of internal buffer (not counting the shadow buffer). - - private Int32 _readPos; // Read pointer within shared buffer. - private Int32 _readLen; // Number of bytes read in buffer from _stream. - private Int32 _writePos; // Write pointer within shared buffer. - - private BeginEndAwaitableAdapter _beginEndAwaitable; // Used to be able to await a BeginXxx call and thus to share code - // between the APM and Async pattern implementations - - private Task<Int32> _lastSyncCompletedReadTask; // The last successful Task returned from ReadAsync - // (perf optimization for successive reads of the same size) - - - // Removing a private default constructor is a breaking change for the DataContractSerializer. - // Because this ctor was here previously we need to keep it around. - private BufferedStream() { } - - - public BufferedStream(Stream stream) - - : this(stream, _DefaultBufferSize) { - } - - - public BufferedStream(Stream stream, Int32 bufferSize) { - - if (stream == null) - throw new ArgumentNullException("stream"); - - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_MustBePositive", "bufferSize")); - - Contract.EndContractBlock(); - - BCLDebug.Perf(!(stream is FileStream), "FileStream is buffered - don't wrap it in a BufferedStream"); - BCLDebug.Perf(!(stream is MemoryStream), "MemoryStream shouldn't be wrapped in a BufferedStream!"); - BCLDebug.Perf(!(stream is BufferedStream), "BufferedStream shouldn't be wrapped in another BufferedStream!"); - - _stream = stream; - _bufferSize = bufferSize; - - // Allocate _buffer on its first use - it will not be used if all reads - // & writes are greater than or equal to buffer size. - - if (!_stream.CanRead && !_stream.CanWrite) - __Error.StreamIsClosed(); - } - - - private void EnsureNotClosed() { - - if (_stream == null) - __Error.StreamIsClosed(); - } - - - private void EnsureCanSeek() { - - Contract.Requires(_stream != null); - - if (!_stream.CanSeek) - __Error.SeekNotSupported(); - } - - - private void EnsureCanRead() { - - Contract.Requires(_stream != null); - - if (!_stream.CanRead) - __Error.ReadNotSupported(); - } - - - private void EnsureCanWrite() { - - Contract.Requires(_stream != null); - - if (!_stream.CanWrite) - __Error.WriteNotSupported(); - } - - - private void EnsureBeginEndAwaitableAllocated() { - // We support only a single ongoing async operation and enforce this with a semaphore, - // so singleton is fine and no need to worry about a race condition here. - if (_beginEndAwaitable == null) - _beginEndAwaitable = new BeginEndAwaitableAdapter(); - } - - - /// <summary><code>MaxShadowBufferSize</code> is chosed such that shadow buffers are not allocated on the Large Object Heap. - /// Currently, an object is allocated on the LOH if it is larger than 85000 bytes. See LARGE_OBJECT_SIZE in ndp\clr\src\vm\gc.h - /// We will go with exactly 80 KBytes, although this is somewhat arbitrary.</summary> - private const Int32 MaxShadowBufferSize = 81920; // Make sure not to get to the Large Object Heap. - private void EnsureShadowBufferAllocated() { - - Contract.Assert(_buffer != null); - Contract.Assert(_bufferSize > 0); - - // Already have shadow buffer? - if (_buffer.Length != _bufferSize || _bufferSize >= MaxShadowBufferSize) - return; - - Byte[] shadowBuffer = new Byte[Math.Min(_bufferSize + _bufferSize, MaxShadowBufferSize)]; - Buffer.InternalBlockCopy(_buffer, 0, shadowBuffer, 0, _writePos); - _buffer = shadowBuffer; - } - - - private void EnsureBufferAllocated() { - - Contract.Assert(_bufferSize > 0); - - // BufferedStream is not intended for multi-threaded use, so no worries about the get/set race conditions on _buffer. - if (_buffer == null) - _buffer = new Byte[_bufferSize]; - } - - - internal Stream UnderlyingStream { - [FriendAccessAllowed] - [Pure] - get { return _stream; } - } - - - internal Int32 BufferSize { - [FriendAccessAllowed] - [Pure] - get { return _bufferSize; } - } - - - public override bool CanRead { - [Pure] - get { return _stream != null && _stream.CanRead; } - } - - - public override bool CanWrite { - [Pure] - get { return _stream != null && _stream.CanWrite; } - } - - - public override bool CanSeek { - [Pure] - get { return _stream != null && _stream.CanSeek; } - } - - - public override Int64 Length { - get { - EnsureNotClosed(); - - if (_writePos > 0) - FlushWrite(); - - return _stream.Length; - } - } - - - public override Int64 Position { - get { - EnsureNotClosed(); - EnsureCanSeek(); - - Contract.Assert(! (_writePos > 0 && _readPos != _readLen), "Read and Write buffers cannot both have data in them at the same time."); - return _stream.Position + (_readPos - _readLen + _writePos); - } - set { - if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - - EnsureNotClosed(); - EnsureCanSeek(); - - if (_writePos > 0) - FlushWrite(); - - _readPos = 0; - _readLen = 0; - _stream.Seek(value, SeekOrigin.Begin); - } - } - - - protected override void Dispose(bool disposing) { - - try { - if (disposing && _stream != null) { - try { - Flush(); - } finally { - _stream.Close(); - } - } - } finally { - _stream = null; - _buffer = null; - _lastSyncCompletedReadTask = null; - - // Call base.Dispose(bool) to cleanup async IO resources - base.Dispose(disposing); - } - } - - - public override void Flush() { - - EnsureNotClosed(); - - // Has WRITE data in the buffer: - if (_writePos > 0) { - - FlushWrite(); - Contract.Assert(_writePos == 0 && _readPos == 0 && _readLen == 0); - return; - } - - // Has READ data in the buffer: - if (_readPos < _readLen) { - - // If the underlying stream is not seekable AND we have something in the read buffer, then FlushRead would throw. - // We can either throw away the buffer resulting in data loss (!) or ignore the Flush. - // (We cannot throw becasue it would be a breaking change.) We opt into ignoring the Flush in that situation. - if (!_stream.CanSeek) - return; - - FlushRead(); - - // User streams may have opted to throw from Flush if CanWrite is false (although the abstract Stream does not do so). - // However, if we do not forward the Flush to the underlying stream, we may have problems when chaining several streams. - // Let us make a best effort attempt: - if (_stream.CanWrite || _stream is BufferedStream) - _stream.Flush(); - - Contract.Assert(_writePos == 0 && _readPos == 0 && _readLen == 0); - return; - } - - // We had no data in the buffer, but we still need to tell the underlying stream to flush. - if (_stream.CanWrite || _stream is BufferedStream) - _stream.Flush(); - - _writePos = _readPos = _readLen = 0; - } - - public override Task FlushAsync(CancellationToken cancellationToken) { - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<Int32>(cancellationToken); - - EnsureNotClosed(); - - return FlushAsyncInternal(cancellationToken, this, _stream, _writePos, _readPos, _readLen); - } - - - private static async Task FlushAsyncInternal(CancellationToken cancellationToken, - BufferedStream _this, Stream stream, Int32 writePos, Int32 readPos, Int32 readLen) { - - // We bring instance fields down as local parameters to this async method becasue BufferedStream is derived from MarshalByRefObject. - // Field access would be from the async state machine i.e., not via the this pointer and would require runtime checking to see - // if we are talking to a remote object, which is currently very slow - - Contract.Assert(stream != null); - - SemaphoreSlim sem = _this.EnsureAsyncActiveSemaphoreInitialized(); - await sem.WaitAsync().ConfigureAwait(false); - try { - - if (writePos > 0) { - - await _this.FlushWriteAsync(cancellationToken).ConfigureAwait(false); - Contract.Assert(_this._writePos == 0 && _this._readPos == 0 && _this._readLen == 0); - return; - } - - if (readPos < readLen) { - - // If the underlying stream is not seekable AND we have something in the read buffer, then FlushRead would throw. - // We can either throw away the buffer resulting in date loss (!) or ignore the Flush. (We cannot throw becasue it - // would be a breaking change.) We opt into ignoring the Flush in that situation. - if (!stream.CanSeek) - return; - - _this.FlushRead(); // not async; it uses Seek, but there's no SeekAsync - - // User streams may have opted to throw from Flush if CanWrite is false (although the abstract Stream does not do so). - // However, if we do not forward the Flush to the underlying stream, we may have problems when chaining several streams. - // Let us make a best effort attempt: - if (stream.CanRead || stream is BufferedStream) - await stream.FlushAsync(cancellationToken).ConfigureAwait(false); - - Contract.Assert(_this._writePos == 0 && _this._readPos == 0 && _this._readLen == 0); - return; - } - - // We had no data in the buffer, but we still need to tell the underlying stream to flush. - if (stream.CanWrite || stream is BufferedStream) - await stream.FlushAsync(cancellationToken).ConfigureAwait(false); - - // There was nothing in the buffer: - Contract.Assert(_this._writePos == 0 && _this._readPos == _this._readLen); - - } finally { - sem.Release(); - } - } - - - // Reading is done in blocks, but someone could read 1 byte from the buffer then write. - // At that point, the underlying stream's pointer is out of sync with this stream's position. - // All write functions should call this function to ensure that the buffered data is not lost. - private void FlushRead() { - - Contract.Assert(_writePos == 0, "BufferedStream: Write buffer must be empty in FlushRead!"); - - if (_readPos - _readLen != 0) - _stream.Seek(_readPos - _readLen, SeekOrigin.Current); - - _readPos = 0; - _readLen = 0; - } - - - private void ClearReadBufferBeforeWrite() { - - // This is called by write methods to clear the read buffer. - - Contract.Assert(_readPos <= _readLen, "_readPos <= _readLen [" + _readPos +" <= " + _readLen + "]"); - - // No READ data in the buffer: - if (_readPos == _readLen) { - - _readPos = _readLen = 0; - return; - } - - // Must have READ data. - Contract.Assert(_readPos < _readLen); - - // If the underlying stream cannot seek, FlushRead would end up throwing NotSupported. - // However, since the user did not call a method that is intuitively expected to seek, a better message is in order. - // Ideally, we would throw an InvalidOperation here, but for backward compat we have to stick with NotSupported. - if (!_stream.CanSeek) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_CannotWriteToBufferedStreamIfReadBufferCannotBeFlushed")); - - FlushRead(); - } - - - private void FlushWrite() { - - Contract.Assert(_readPos == 0 && _readLen == 0, - "BufferedStream: Read buffer must be empty in FlushWrite!"); - Contract.Assert(_buffer != null && _bufferSize >= _writePos, - "BufferedStream: Write buffer must be allocated and write position must be in the bounds of the buffer in FlushWrite!"); - - _stream.Write(_buffer, 0, _writePos); - _writePos = 0; - _stream.Flush(); - } - - - private async Task FlushWriteAsync(CancellationToken cancellationToken) { - - Contract.Assert(_readPos == 0 && _readLen == 0, - "BufferedStream: Read buffer must be empty in FlushWrite!"); - Contract.Assert(_buffer != null && _bufferSize >= _writePos, - "BufferedStream: Write buffer must be allocated and write position must be in the bounds of the buffer in FlushWrite!"); - - await _stream.WriteAsync(_buffer, 0, _writePos, cancellationToken).ConfigureAwait(false); - _writePos = 0; - await _stream.FlushAsync(cancellationToken).ConfigureAwait(false); - } - - - private Int32 ReadFromBuffer(Byte[] array, Int32 offset, Int32 count) { - - Int32 readBytes = _readLen - _readPos; - Contract.Assert(readBytes >= 0); - - if (readBytes == 0) - return 0; - - Contract.Assert(readBytes > 0); - - if (readBytes > count) - readBytes = count; - - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, readBytes); - _readPos += readBytes; - - return readBytes; - } - - - private Int32 ReadFromBuffer(Byte[] array, Int32 offset, Int32 count, out Exception error) { - - try { - - error = null; - return ReadFromBuffer(array, offset, count); - - } catch (Exception ex) { - error = ex; - return 0; - } - } - - - public override int Read([In, Out] Byte[] array, Int32 offset, Int32 count) { - - if (array == null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - EnsureNotClosed(); - EnsureCanRead(); - - Int32 bytesFromBuffer = ReadFromBuffer(array, offset, count); - - // We may have read less than the number of bytes the user asked for, but that is part of the Stream contract. - - // Reading again for more data may cause us to block if we're using a device with no clear end of file, - // such as a serial port or pipe. If we blocked here and this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving two processes. - // BUT - this is a breaking change. - // So: If we could not read all bytes the user asked for from the buffer, we will try once from the underlying - // stream thus ensuring the same blocking behaviour as if the underlying stream was not wrapped in this BufferedStream. - if (bytesFromBuffer == count) - return bytesFromBuffer; - - Int32 alreadySatisfied = bytesFromBuffer; - if (bytesFromBuffer > 0) { - count -= bytesFromBuffer; - offset += bytesFromBuffer; - } - - // So the READ buffer is empty. - Contract.Assert(_readLen == _readPos); - _readPos = _readLen = 0; - - // If there was anything in the WRITE buffer, clear it. - if (_writePos > 0) - FlushWrite(); - - // If the requested read is larger than buffer size, avoid the buffer and still use a single read: - if (count >= _bufferSize) { - - return _stream.Read(array, offset, count) + alreadySatisfied; - } - - // Ok. We can fill the buffer: - EnsureBufferAllocated(); - _readLen = _stream.Read(_buffer, 0, _bufferSize); - - bytesFromBuffer = ReadFromBuffer(array, offset, count); - - // We may have read less than the number of bytes the user asked for, but that is part of the Stream contract. - // Reading again for more data may cause us to block if we're using a device with no clear end of stream, - // such as a serial port or pipe. If we blocked here & this code was used with redirected pipes for a process's - // standard output, this can lead to deadlocks involving two processes. Additionally, translating one read on the - // BufferedStream to more than one read on the underlying Stream may defeat the whole purpose of buffering of the - // underlying reads are significantly more expensive. - - return bytesFromBuffer + alreadySatisfied; - } - - - public override IAsyncResult BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) { - - if (buffer == null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // Previous version incorrectly threw NotSupported instead of ObjectDisposed. We keep that behaviour for back-compat. - // EnsureNotClosed(); - if (_stream == null) __Error.ReadNotSupported(); - EnsureCanRead(); - - Int32 bytesFromBuffer = 0; - // Try to satisfy the request from the buffer synchronously. But still need a sem-lock in case that another - // Async IO Task accesses the buffer concurrently. If we fail to acquire the lock without waiting, make this - // an Async operation. - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - Task semaphoreLockTask = sem.WaitAsync(); - if (semaphoreLockTask.Status == TaskStatus.RanToCompletion) { - - bool completeSynchronously = true; - try { - - Exception error; - bytesFromBuffer = ReadFromBuffer(buffer, offset, count, out error); - - // If we satistied enough data from the buffer, we can complete synchronously. - // Reading again for more data may cause us to block if we're using a device with no clear end of file, - // such as a serial port or pipe. If we blocked here and this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving two processes. - // BUT - this is a breaking change. - // So: If we could not read all bytes the user asked for from the buffer, we will try once from the underlying - // stream thus ensuring the same blocking behaviour as if the underlying stream was not wrapped in this BufferedStream. - completeSynchronously = (bytesFromBuffer == count || error != null); - - if (completeSynchronously) { - - SynchronousAsyncResult asyncResult = (error == null) - ? new SynchronousAsyncResult(bytesFromBuffer, state) - : new SynchronousAsyncResult(error, state, isWrite: false); - if (callback != null) - callback(asyncResult); - - return asyncResult; - } - } finally { - if (completeSynchronously) // if this is FALSE, we will be entering ReadFromUnderlyingStreamAsync and releasing there. - sem.Release(); - } - } - - // Delegate to the async implementation. - return BeginReadFromUnderlyingStream(buffer, offset + bytesFromBuffer, count - bytesFromBuffer, callback, state, - bytesFromBuffer, semaphoreLockTask); - } - - - private IAsyncResult BeginReadFromUnderlyingStream(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state, - Int32 bytesAlreadySatisfied, Task semaphoreLockTask) { - - Task<Int32> readOp = ReadFromUnderlyingStreamAsync(buffer, offset, count, CancellationToken.None, - bytesAlreadySatisfied, semaphoreLockTask, useApmPattern: true); - return TaskToApm.Begin(readOp, callback, state); - } - - - public override Int32 EndRead(IAsyncResult asyncResult) { - - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - Contract.Ensures(Contract.Result<Int32>() >= 0); - Contract.EndContractBlock(); - - var sAR = asyncResult as SynchronousAsyncResult; - if (sAR != null) - return SynchronousAsyncResult.EndRead(asyncResult); - return TaskToApm.End<Int32>(asyncResult); - } - - - private Task<Int32> LastSyncCompletedReadTask(Int32 val) { - - Task<Int32> t = _lastSyncCompletedReadTask; - Contract.Assert(t == null || t.Status == TaskStatus.RanToCompletion); - - if (t != null && t.Result == val) - return t; - - t = Task.FromResult<Int32>(val); - _lastSyncCompletedReadTask = t; - return t; - } - - - public override Task<Int32> ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { - - if (buffer == null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // Fast path check for cancellation already requested - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<Int32>(cancellationToken); - - EnsureNotClosed(); - EnsureCanRead(); - - Int32 bytesFromBuffer = 0; - // Try to satisfy the request from the buffer synchronously. But still need a sem-lock in case that another - // Async IO Task accesses the buffer concurrently. If we fail to acquire the lock without waiting, make this - // an Async operation. - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - Task semaphoreLockTask = sem.WaitAsync(); - if (semaphoreLockTask.Status == TaskStatus.RanToCompletion) { - - bool completeSynchronously = true; - try { - Exception error; - bytesFromBuffer = ReadFromBuffer(buffer, offset, count, out error); - - // If we satistied enough data from the buffer, we can complete synchronously. - // Reading again for more data may cause us to block if we're using a device with no clear end of file, - // such as a serial port or pipe. If we blocked here and this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving two processes. - // BUT - this is a breaking change. - // So: If we could not read all bytes the user asked for from the buffer, we will try once from the underlying - // stream thus ensuring the same blocking behaviour as if the underlying stream was not wrapped in this BufferedStream. - completeSynchronously = (bytesFromBuffer == count || error != null); - - if (completeSynchronously) { - - return (error == null) - ? LastSyncCompletedReadTask(bytesFromBuffer) - : Task.FromException<Int32>(error); - } - } finally { - if (completeSynchronously) // if this is FALSE, we will be entering ReadFromUnderlyingStreamAsync and releasing there. - sem.Release(); - } - } - - // Delegate to the async implementation. - return ReadFromUnderlyingStreamAsync(buffer, offset + bytesFromBuffer, count - bytesFromBuffer, cancellationToken, - bytesFromBuffer, semaphoreLockTask, useApmPattern: false); - } - - - /// <summary>BufferedStream should be as thin a wrapper as possible. We want that ReadAsync delegates to - /// ReadAsync of the underlying _stream and that BeginRead delegates to BeginRead of the underlying stream, - /// rather than calling the base Stream which implements the one in terms of the other. This allows BufferedStream - /// to affect the semantics of the stream it wraps as little as possible. At the same time, we want to share as - /// much code between the APM and the Async pattern implementations as possible. This method is called by both with - /// a corresponding useApmPattern value. Recall that Task implements IAsyncResult.</summary> - /// <returns>-2 if _bufferSize was set to 0 while waiting on the semaphore; otherwise num of bytes read.</returns> - private async Task<Int32> ReadFromUnderlyingStreamAsync(Byte[] array, Int32 offset, Int32 count, - CancellationToken cancellationToken, - Int32 bytesAlreadySatisfied, - Task semaphoreLockTask, bool useApmPattern) { - - // Same conditions validated with exceptions in ReadAsync: - // (These should be Contract.Requires(..) but that method had some issues in async methods; using Assert(..) for now.) - Contract.Assert(array != null); - Contract.Assert(offset >= 0); - Contract.Assert(count >= 0); - Contract.Assert(array.Length - offset >= count); - Contract.Assert(_stream != null); - Contract.Assert(_stream.CanRead); - Contract.Assert(_bufferSize > 0); - Contract.Assert(semaphoreLockTask != null); - - // Employ async waiting based on the same synchronization used in BeginRead of the abstract Stream. - await semaphoreLockTask.ConfigureAwait(false); - try { - - // The buffer might have been changed by another async task while we were waiting on the semaphore. - // Check it now again. - Int32 bytesFromBuffer = ReadFromBuffer(array, offset, count); - if (bytesFromBuffer == count) - return bytesAlreadySatisfied + bytesFromBuffer; - - if (bytesFromBuffer > 0) { - count -= bytesFromBuffer; - offset += bytesFromBuffer; - bytesAlreadySatisfied += bytesFromBuffer; - } - - Contract.Assert(_readLen == _readPos); - _readPos = _readLen = 0; - - // If there was anything in the WRITE buffer, clear it. - if (_writePos > 0) - await FlushWriteAsync(cancellationToken).ConfigureAwait(false); // no Begin-End read version for Flush. Use Async. - - // If the requested read is larger than buffer size, avoid the buffer and still use a single read: - if (count >= _bufferSize) { - - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginRead(array, offset, count, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - return bytesAlreadySatisfied + _stream.EndRead(await _beginEndAwaitable); - } else { - return bytesAlreadySatisfied + await _stream.ReadAsync(array, offset, count, cancellationToken).ConfigureAwait(false); - } - } - - // Ok. We can fill the buffer: - EnsureBufferAllocated(); - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginRead(_buffer, 0, _bufferSize, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - _readLen = _stream.EndRead(await _beginEndAwaitable); - } else { - _readLen = await _stream.ReadAsync(_buffer, 0, _bufferSize, cancellationToken).ConfigureAwait(false); - } - - bytesFromBuffer = ReadFromBuffer(array, offset, count); - return bytesAlreadySatisfied + bytesFromBuffer; - - } finally { - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - sem.Release(); - } - } - - - public override Int32 ReadByte() { - - EnsureNotClosed(); - EnsureCanRead(); - - if (_readPos == _readLen) { - - if (_writePos > 0) - FlushWrite(); - - EnsureBufferAllocated(); - _readLen = _stream.Read(_buffer, 0, _bufferSize); - _readPos = 0; - } - - if (_readPos == _readLen) - return -1; - - Int32 b = _buffer[_readPos++]; - return b; - } - - - private void WriteToBuffer(Byte[] array, ref Int32 offset, ref Int32 count) { - - Int32 bytesToWrite = Math.Min(_bufferSize - _writePos, count); - - if (bytesToWrite <= 0) - return; - - EnsureBufferAllocated(); - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, bytesToWrite); - - _writePos += bytesToWrite; - count -= bytesToWrite; - offset += bytesToWrite; - } - - - private void WriteToBuffer(Byte[] array, ref Int32 offset, ref Int32 count, out Exception error) { - - try { - - error = null; - WriteToBuffer(array, ref offset, ref count); - - } catch (Exception ex) { - error = ex; - } - } - - - public override void Write(Byte[] array, Int32 offset, Int32 count) { - - if (array == null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - EnsureNotClosed(); - EnsureCanWrite(); - - if (_writePos == 0) - ClearReadBufferBeforeWrite(); - - #region Write algorithm comment - // We need to use the buffer, while avoiding unnecessary buffer usage / memory copies. - // We ASSUME that memory copies are much cheaper than writes to the underlying stream, so if an extra copy is - // guaranteed to reduce the number of writes, we prefer it. - // We pick a simple strategy that makes degenerate cases rare if our assumptions are right. - // - // For every write, we use a simple heuristic (below) to decide whether to use the buffer. - // The heuristic has the desirable property (*) that if the specified user data can fit into the currently available - // buffer space without filling it up completely, the heuristic will always tell us to use the buffer. It will also - // tell us to use the buffer in cases where the current write would fill the buffer, but the remaining data is small - // enough such that subsequent operations can use the buffer again. - // - // Algorithm: - // Determine whether or not to buffer according to the heuristic (below). - // If we decided to use the buffer: - // Copy as much user data as we can into the buffer. - // If we consumed all data: We are finished. - // Otherwise, write the buffer out. - // Copy the rest of user data into the now cleared buffer (no need to write out the buffer again as the heuristic - // will prevent it from being filled twice). - // If we decided not to use the buffer: - // Can the data already in the buffer and current user data be combines to a single write - // by allocating a "shadow" buffer of up to twice the size of _bufferSize (up to a limit to avoid LOH)? - // Yes, it can: - // Allocate a larger "shadow" buffer and ensure the buffered data is moved there. - // Copy user data to the shadow buffer. - // Write shadow buffer to the underlying stream in a single operation. - // No, it cannot (amount of data is still too large): - // Write out any data possibly in the buffer. - // Write out user data directly. - // - // Heuristic: - // If the subsequent write operation that follows the current write operation will result in a write to the - // underlying stream in case that we use the buffer in the current write, while it would not have if we avoided - // using the buffer in the current write (by writing current user data to the underlying stream directly), then we - // prefer to avoid using the buffer since the corresponding memory copy is wasted (it will not reduce the number - // of writes to the underlying stream, which is what we are optimising for). - // ASSUME that the next write will be for the same amount of bytes as the current write (most common case) and - // determine if it will cause a write to the underlying stream. If the next write is actually larger, our heuristic - // still yields the right behaviour, if the next write is actually smaller, we may making an unnecessary write to - // the underlying stream. However, this can only occur if the current write is larger than half the buffer size and - // we will recover after one iteration. - // We have: - // useBuffer = (_writePos + count + count < _bufferSize + _bufferSize) - // - // Example with _bufferSize = 20, _writePos = 6, count = 10: - // - // +---------------------------------------+---------------------------------------+ - // | current buffer | next iteration's "future" buffer | - // +---------------------------------------+---------------------------------------+ - // |0| | | | | | | | | |1| | | | | | | | | |2| | | | | | | | | |3| | | | | | | | | | - // |0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9|0|1|2|3|4|5|6|7|8|9| - // +-----------+-------------------+-------------------+---------------------------+ - // | _writePos | current count | assumed next count|avail buff after next write| - // +-----------+-------------------+-------------------+---------------------------+ - // - // A nice property (*) of this heuristic is that it will always succeed if the user data completely fits into the - // available buffer, i.e. if count < (_bufferSize - _writePos). - #endregion Write algorithm comment - - Contract.Assert(_writePos < _bufferSize); - - Int32 totalUserBytes; - bool useBuffer; - checked { // We do not expect buffer sizes big enough for an overflow, but if it happens, lets fail early: - totalUserBytes = _writePos + count; - useBuffer = (totalUserBytes + count < (_bufferSize + _bufferSize)); - } - - if (useBuffer) { - - WriteToBuffer(array, ref offset, ref count); - - if (_writePos < _bufferSize) { - - Contract.Assert(count == 0); - return; - } - - Contract.Assert(count >= 0); - Contract.Assert(_writePos == _bufferSize); - Contract.Assert(_buffer != null); - - _stream.Write(_buffer, 0, _writePos); - _writePos = 0; - - WriteToBuffer(array, ref offset, ref count); - - Contract.Assert(count == 0); - Contract.Assert(_writePos < _bufferSize); - - } else { // if (!useBuffer) - - // Write out the buffer if necessary. - if (_writePos > 0) { - - Contract.Assert(_buffer != null); - Contract.Assert(totalUserBytes >= _bufferSize); - - // Try avoiding extra write to underlying stream by combining previously buffered data with current user data: - if (totalUserBytes <= (_bufferSize + _bufferSize) && totalUserBytes <= MaxShadowBufferSize) { - - EnsureShadowBufferAllocated(); - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count); - _stream.Write(_buffer, 0, totalUserBytes); - _writePos = 0; - return; - } - - _stream.Write(_buffer, 0, _writePos); - _writePos = 0; - } - - // Write out user data. - _stream.Write(array, offset, count); - } - } - - - - - public override IAsyncResult BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) { - - if (buffer == null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // Previous version incorrectly threw NotSupported instead of ObjectDisposed. We keep that behaviour for back-compat. - // EnsureNotClosed(); - if (_stream == null) __Error.ReadNotSupported(); - EnsureCanWrite(); - - // Try to satisfy the request from the buffer synchronously. But still need a sem-lock in case that another - // Async IO Task accesses the buffer concurrently. If we fail to acquire the lock without waiting, make this - // an Async operation. - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - Task semaphoreLockTask = sem.WaitAsync(); - if (semaphoreLockTask.Status == TaskStatus.RanToCompletion) { - - bool completeSynchronously = true; - try { - if (_writePos == 0) - ClearReadBufferBeforeWrite(); - - // If the write completely fits into the buffer, we can complete synchronously. - Contract.Assert(_writePos < _bufferSize); - completeSynchronously = (count < _bufferSize - _writePos); - - if (completeSynchronously) { - - Exception error; - WriteToBuffer(buffer, ref offset, ref count, out error); - Contract.Assert(count == 0); - - SynchronousAsyncResult asyncResult = (error == null) - ? new SynchronousAsyncResult(state) - : new SynchronousAsyncResult(error, state, isWrite: true); - if (callback != null) - callback(asyncResult); - - return asyncResult; - } - } finally { - if (completeSynchronously) // if this is FALSE, we will be entering WriteToUnderlyingStreamAsync and releasing there. - sem.Release(); - } - } - - // Delegate to the async implementation. - return BeginWriteToUnderlyingStream(buffer, offset, count, callback, state, semaphoreLockTask); - } - - - private IAsyncResult BeginWriteToUnderlyingStream(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state, - Task semaphoreLockTask) { - - Task writeOp = WriteToUnderlyingStreamAsync(buffer, offset, count, CancellationToken.None, semaphoreLockTask, useApmPattern: true); - return TaskToApm.Begin(writeOp, callback, state); - } - - - public override void EndWrite(IAsyncResult asyncResult) { - - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - Contract.EndContractBlock(); - - var sAR = asyncResult as SynchronousAsyncResult; - if (sAR != null) { - SynchronousAsyncResult.EndWrite(asyncResult); - return; - } - - TaskToApm.End(asyncResult); - } - - - public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { - - if (buffer == null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // Fast path check for cancellation already requested - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<Int32>(cancellationToken); - - EnsureNotClosed(); - EnsureCanWrite(); - - // Try to satisfy the request from the buffer synchronously. But still need a sem-lock in case that another - // Async IO Task accesses the buffer concurrently. If we fail to acquire the lock without waiting, make this - // an Async operation. - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - Task semaphoreLockTask = sem.WaitAsync(); - if (semaphoreLockTask.Status == TaskStatus.RanToCompletion) { - - bool completeSynchronously = true; - try { - - if (_writePos == 0) - ClearReadBufferBeforeWrite(); - - Contract.Assert(_writePos < _bufferSize); - - // If the write completely fits into the buffer, we can complete synchronously: - completeSynchronously = (count < _bufferSize - _writePos); - - if (completeSynchronously) { - - Exception error; - WriteToBuffer(buffer, ref offset, ref count, out error); - Contract.Assert(count == 0); - - return (error == null) - ? Task.CompletedTask - : Task.FromException(error); - } - } finally { - if (completeSynchronously) // if this is FALSE, we will be entering WriteToUnderlyingStreamAsync and releasing there. - sem.Release(); - } - } - - // Delegate to the async implementation. - return WriteToUnderlyingStreamAsync(buffer, offset, count, cancellationToken, semaphoreLockTask, useApmPattern: false); - } - - - /// <summary>BufferedStream should be as thin a wrapper as possible. We want that WriteAsync delegates to - /// WriteAsync of the underlying _stream and that BeginWrite delegates to BeginWrite of the underlying stream, - /// rather than calling the base Stream which implements the one in terms of the other. This allows BufferedStream - /// to affect the semantics of the stream it wraps as little as possible. At the same time, we want to share as - /// much code between the APM and the Async pattern implementations as possible. This method is called by both with - /// a corresponding useApmPattern value. Recall that Task implements IAsyncResult.</summary> - private async Task WriteToUnderlyingStreamAsync(Byte[] array, Int32 offset, Int32 count, - CancellationToken cancellationToken, - Task semaphoreLockTask, bool useApmPattern) { - - // (These should be Contract.Requires(..) but that method had some issues in async methods; using Assert(..) for now.) - Contract.Assert(array != null); - Contract.Assert(offset >= 0); - Contract.Assert(count >= 0); - Contract.Assert(array.Length - offset >= count); - Contract.Assert(_stream != null); - Contract.Assert(_stream.CanWrite); - Contract.Assert(_bufferSize > 0); - Contract.Assert(semaphoreLockTask != null); - - // See the LARGE COMMENT in Write(..) for the explanation of the write buffer algorithm. - - await semaphoreLockTask.ConfigureAwait(false); - try { - - // The buffer might have been changed by another async task while we were waiting on the semaphore. - // However, note that if we recalculate the sync completion condition to TRUE, then useBuffer will also be TRUE. - - if (_writePos == 0) - ClearReadBufferBeforeWrite(); - - Int32 totalUserBytes; - bool useBuffer; - checked { // We do not expect buffer sizes big enough for an overflow, but if it happens, lets fail early: - totalUserBytes = _writePos + count; - useBuffer = (totalUserBytes + count < (_bufferSize + _bufferSize)); - } - - if (useBuffer) { - - WriteToBuffer(array, ref offset, ref count); - - if (_writePos < _bufferSize) { - - Contract.Assert(count == 0); - return; - } - - Contract.Assert(count >= 0); - Contract.Assert(_writePos == _bufferSize); - Contract.Assert(_buffer != null); - - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginWrite(_buffer, 0, _writePos, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - _stream.EndWrite(await _beginEndAwaitable); - } else { - await _stream.WriteAsync(_buffer, 0, _writePos, cancellationToken).ConfigureAwait(false); - } - _writePos = 0; - - WriteToBuffer(array, ref offset, ref count); - - Contract.Assert(count == 0); - Contract.Assert(_writePos < _bufferSize); - - } else { // if (!useBuffer) - - // Write out the buffer if necessary. - if (_writePos > 0) { - - Contract.Assert(_buffer != null); - Contract.Assert(totalUserBytes >= _bufferSize); - - // Try avoiding extra write to underlying stream by combining previously buffered data with current user data: - if (totalUserBytes <= (_bufferSize + _bufferSize) && totalUserBytes <= MaxShadowBufferSize) { - - EnsureShadowBufferAllocated(); - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count); - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginWrite(_buffer, 0, totalUserBytes, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - _stream.EndWrite(await _beginEndAwaitable); - } else { - await _stream.WriteAsync(_buffer, 0, totalUserBytes, cancellationToken).ConfigureAwait(false); - } - _writePos = 0; - return; - } - - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginWrite(_buffer, 0, _writePos, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - _stream.EndWrite(await _beginEndAwaitable); - } else { - await _stream.WriteAsync(_buffer, 0, _writePos, cancellationToken).ConfigureAwait(false); - } - _writePos = 0; - } - - // Write out user data. - if (useApmPattern) { - EnsureBeginEndAwaitableAllocated(); - _stream.BeginWrite(array, offset, count, BeginEndAwaitableAdapter.Callback, _beginEndAwaitable); - _stream.EndWrite(await _beginEndAwaitable); - } else { - await _stream.WriteAsync(array, offset, count, cancellationToken).ConfigureAwait(false); - } - } - } finally { - SemaphoreSlim sem = base.EnsureAsyncActiveSemaphoreInitialized(); - sem.Release(); - } - } - - - public override void WriteByte(Byte value) { - - EnsureNotClosed(); - - if (_writePos == 0) { - - EnsureCanWrite(); - ClearReadBufferBeforeWrite(); - EnsureBufferAllocated(); - } - - // We should not be flushing here, but only writing to the underlying stream, but previous version flushed, so we keep this. - if (_writePos >= _bufferSize - 1) - FlushWrite(); - - _buffer[_writePos++] = value; - - Contract.Assert(_writePos < _bufferSize); - } - - - public override Int64 Seek(Int64 offset, SeekOrigin origin) { - - EnsureNotClosed(); - EnsureCanSeek(); - - // If we have bytes in the WRITE buffer, flush them out, seek and be done. - if (_writePos > 0) { - - // We should be only writing the buffer and not flushing, - // but the previous version did flush and we stick to it for back-compat reasons. - FlushWrite(); - return _stream.Seek(offset, origin); - } - - // The buffer is either empty or we have a buffered READ. - - if (_readLen - _readPos > 0 && origin == SeekOrigin.Current) { - - // If we have bytes in the READ buffer, adjust the seek offset to account for the resulting difference - // between this stream's position and the underlying stream's position. - offset -= (_readLen - _readPos); - } - - Int64 oldPos = Position; - Contract.Assert(oldPos == _stream.Position + (_readPos - _readLen)); - - Int64 newPos = _stream.Seek(offset, origin); - - // If the seek destination is still within the data currently in the buffer, we want to keep the buffer data and continue using it. - // Otherwise we will throw away the buffer. This can only happen on READ, as we flushed WRITE data above. - - // The offset of the new/updated seek pointer within _buffer: - _readPos = (Int32) (newPos - (oldPos - _readPos)); - - // If the offset of the updated seek pointer in the buffer is still legal, then we can keep using the buffer: - if (0 <= _readPos && _readPos < _readLen) { - - // Adjust the seek pointer of the underlying stream to reflect the amount of useful bytes in the read buffer: - _stream.Seek(_readLen - _readPos, SeekOrigin.Current); - - } else { // The offset of the updated seek pointer is not a legal offset. Loose the buffer. - - _readPos = _readLen = 0; - } - - Contract.Assert(newPos == Position, "newPos (=" + newPos + ") == Position (=" + Position + ")"); - return newPos; - } - - - public override void SetLength(Int64 value) { - - if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NegFileSize")); - Contract.EndContractBlock(); - - EnsureNotClosed(); - EnsureCanSeek(); - EnsureCanWrite(); - - Flush(); - _stream.SetLength(value); - } - -} // class BufferedStream -} // namespace diff --git a/src/mscorlib/src/System/IO/Directory.cs b/src/mscorlib/src/System/IO/Directory.cs index be74538d2d..d6b68222cd 100644 --- a/src/mscorlib/src/System/IO/Directory.cs +++ b/src/mscorlib/src/System/IO/Directory.cs @@ -15,168 +15,70 @@ ** ===========================================================*/ -using System; -using System.Collections; using System.Collections.Generic; using System.Security; using System.Security.Permissions; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; -using System.Text; using System.Runtime.InteropServices; -using System.Globalization; -using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.Contracts; -using System.Threading; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif - -namespace System.IO { +namespace System.IO +{ [ComVisible(true)] public static class Directory { public static DirectoryInfo GetParent(String path) { if (path==null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length==0) - throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path"); + throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), nameof(path)); Contract.EndContractBlock(); - String fullPath = Path.GetFullPathInternal(path); - - String s = Path.GetDirectoryName(fullPath); + string fullPath = Path.GetFullPath(path); + + string s = Path.GetDirectoryName(fullPath); if (s==null) return null; return new DirectoryInfo(s); } - [System.Security.SecuritySafeCritical] public static DirectoryInfo CreateDirectory(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); Contract.EndContractBlock(); - return InternalCreateDirectoryHelper(path, true); + return InternalCreateDirectoryHelper(path); } - [System.Security.SecurityCritical] - internal static DirectoryInfo UnsafeCreateDirectory(String path) - { - if (path == null) - throw new ArgumentNullException("path"); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); - Contract.EndContractBlock(); - - return InternalCreateDirectoryHelper(path, false); - } - - [System.Security.SecurityCritical] - internal static DirectoryInfo InternalCreateDirectoryHelper(String path, bool checkHost) + internal static DirectoryInfo InternalCreateDirectoryHelper(String path) { Contract.Requires(path != null); Contract.Requires(path.Length != 0); - String fullPath = Path.GetFullPathInternal(path); - - // You need read access to the directory to be returned back and write access to all the directories - // that you need to create. If we fail any security checks we will not create any directories at all. - // We attempt to create directories only after all the security checks have passed. This is avoid doing - // a demand at every level. - String demandDir = GetDemandDir(fullPath, true); - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandDir); - state.EnsureState(); // do the check on the AppDomainManager to make sure this is allowed - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false); -#endif - - InternalCreateDirectory(fullPath, path, null, checkHost); - - return new DirectoryInfo(fullPath, false); - } - -#if FEATURE_MACL - [System.Security.SecuritySafeCritical] // auto-generated - public static DirectoryInfo CreateDirectory(String path, DirectorySecurity directorySecurity) { - if (path==null) - throw new ArgumentNullException("path"); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); - Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); + String fullPath = Path.GetFullPath(path); - // You need read access to the directory to be returned back and write access to all the directories - // that you need to create. If we fail any security checks we will not create any directories at all. - // We attempt to create directories only after all the security checks have passed. This is avoid doing - // a demand at every level. - String demandDir = GetDemandDir(fullPath, true); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandDir, false, false ); + InternalCreateDirectory(fullPath, path, null); - InternalCreateDirectory(fullPath, path, directorySecurity); - return new DirectoryInfo(fullPath, false); } -#endif // FEATURE_MACL - // Input to this method should already be fullpath. This method will ensure that we append - // the trailing slash only when appropriate and when thisDirOnly is specified append a "." - // at the end of the path to indicate that the demand is only for the fullpath and not - // everything underneath it. - internal static String GetDemandDir(string fullPath, bool thisDirOnly) + internal unsafe static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj) { - String demandPath; - - if (thisDirOnly) { - if (fullPath.EndsWith( Path.DirectorySeparatorChar ) - || fullPath.EndsWith( Path.AltDirectorySeparatorChar ) ) - demandPath = fullPath + "."; - else - demandPath = fullPath + Path.DirectorySeparatorCharAsString + "."; - } - else { - if (!(fullPath.EndsWith( Path.DirectorySeparatorChar ) - || fullPath.EndsWith( Path.AltDirectorySeparatorChar )) ) - demandPath = fullPath + Path.DirectorySeparatorCharAsString; - else - demandPath = fullPath; - } - return demandPath; - } - - internal static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj) - { - InternalCreateDirectory(fullPath, path, dirSecurityObj, false); - } - - - [System.Security.SecuritySafeCritical] - internal unsafe static void InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, bool checkHost) - { -#if FEATURE_MACL - DirectorySecurity dirSecurity = (DirectorySecurity)dirSecurityObj; -#endif // FEATURE_MACL - int length = fullPath.Length; // We need to trim the trailing slash or the code will try to create 2 directories of the same name. - if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1])) + if (length >= 2 && PathInternal.IsDirectorySeparator(fullPath[length - 1])) length--; - int lengthRoot = Path.GetRootLength(fullPath); + int lengthRoot = PathInternal.GetRootLength(fullPath); // For UNC paths that are only // or /// - if (length == 2 && Path.IsDirectorySeparator(fullPath[1])) + if (length == 2 && PathInternal.IsDirectorySeparator(fullPath[1])) throw new IOException(Environment.GetResourceString("IO.IO_CannotCreateDirectory", path)); // We can save a bunch of work if the directory we want to create already exists. This also @@ -215,56 +117,9 @@ namespace System.IO { int count = stackDir.Count; - if (stackDir.Count != 0 -#if FEATURE_CAS_POLICY - // All demands in full trust domains are no-ops, so skip - // - // The full path went through validity checks by being passed through FileIOPermissions already. - // As a sub string of the full path can't fail the checks if the full path passes. - && !CodeAccessSecurityEngine.QuickCheckForAllDemands() -#endif - ) - { - String[] securityList = new String[stackDir.Count]; - stackDir.CopyTo(securityList, 0); - for (int j = 0 ; j < securityList.Length; j++) - securityList[j] += "\\."; // leaf will never have a slash at the end - - // Security check for all directories not present only. -#if FEATURE_MACL - AccessControlActions control = (dirSecurity == null) ? AccessControlActions.None : AccessControlActions.Change; - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, control, securityList, false, false); -#else -#if FEATURE_CORECLR - if (checkHost) - { - foreach (String demandPath in securityList) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, demandPath); - state.EnsureState(); - } - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, securityList, false, false); -#endif -#endif //FEATURE_MACL - } - // If we were passed a DirectorySecurity, convert it to a security // descriptor and set it in he call to CreateDirectory. Win32Native.SECURITY_ATTRIBUTES secAttrs = null; -#if FEATURE_MACL - if (dirSecurity != null) { - secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); - - // For ACL's, get the security descriptor from the FileSecurity. - byte[] sd = dirSecurity.GetSecurityDescriptorBinaryForm(); - byte * bytesOnStack = stackalloc byte[sd.Length]; - Buffer.Memcpy(bytesOnStack, 0, sd, 0, sd.Length); - secAttrs.pSecurityDescriptor = bytesOnStack; - } -#endif bool r = true; int firstError = 0; @@ -290,22 +145,10 @@ namespace System.IO { firstError = currentError; else { // If there's a file in this directory's place, or if we have ERROR_ACCESS_DENIED when checking if the directory already exists throw. - if (File.InternalExists(name) || (!InternalExists(name, out currentError) && currentError == Win32Native.ERROR_ACCESS_DENIED)) { + if (File.InternalExists(name) || (!InternalExists(name, out currentError) && currentError == Win32Native.ERROR_ACCESS_DENIED)) + { firstError = currentError; - // Give the user a nice error message, but don't leak path information. - try { -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, GetDemandDir(name, true)); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, GetDemandDir(name, true)); -#endif // FEATURE_CORECLR - errorString = name; - } - catch(SecurityException) {} + errorString = name; } } } @@ -335,20 +178,12 @@ namespace System.IO { // Your application must have Read permission to the directory's // contents. // - [System.Security.SecuritySafeCritical] // auto-generated public static bool Exists(String path) { - return InternalExistsHelper(path, true); - } - - [System.Security.SecurityCritical] - internal static bool UnsafeExists(String path) - { - return InternalExistsHelper(path, false); + return InternalExistsHelper(path); } - [System.Security.SecurityCritical] - internal static bool InternalExistsHelper(String path, bool checkHost) { + internal static bool InternalExistsHelper(String path) { try { if (path == null) @@ -356,23 +191,7 @@ namespace System.IO { if (path.Length == 0) return false; - // Get fully qualified file name ending in \* for security check - - String fullPath = Path.GetFullPathInternal(path); - String demandPath = GetDemandDir(fullPath, true); - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, demandPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, demandPath, false, false); -#endif - - - return InternalExists(fullPath); + return InternalExists(Path.GetFullPath(path)); } catch (ArgumentException) { } catch (NotSupportedException) { } // Security can throw this on ":" @@ -380,14 +199,13 @@ namespace System.IO { catch (IOException) { } catch (UnauthorizedAccessException) { - Contract.Assert(false, "Ignore this assert and send a repro to Microsoft. This assert was tracking purposes only."); + Debug.Assert(false, "Ignore this assert and send a repro to Microsoft. This assert was tracking purposes only."); } return false; } // Determine whether path describes an existing directory // on disk, avoiding security checks. - [System.Security.SecurityCritical] // auto-generated internal static bool InternalExists(String path) { int lastError = Win32Native.ERROR_SUCCESS; return InternalExists(path, out lastError); @@ -395,7 +213,6 @@ namespace System.IO { // Determine whether path describes an existing directory // on disk, avoiding security checks. - [System.Security.SecurityCritical] // auto-generated internal static bool InternalExists(String path, out int lastError) { Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); lastError = File.FillAttributeInfo(path, ref data, false, true); @@ -404,25 +221,6 @@ namespace System.IO { && ((data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0); } - public static void SetCreationTime(String path,DateTime creationTime) - { - SetCreationTimeUtc(path, creationTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetCreationTimeUtc(String path,DateTime creationTimeUtc) - { - using (SafeFileHandle handle = Directory.OpenHandle(path)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(creationTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, &fileTime, null, null); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - public static DateTime GetCreationTime(String path) { return File.GetCreationTime(path); @@ -433,25 +231,6 @@ namespace System.IO { return File.GetCreationTimeUtc(path); } - public static void SetLastWriteTime(String path,DateTime lastWriteTime) - { - SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetLastWriteTimeUtc(String path,DateTime lastWriteTimeUtc) - { - using (SafeFileHandle handle = Directory.OpenHandle(path)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastWriteTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, null, null, &fileTime); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - public static DateTime GetLastWriteTime(String path) { return File.GetLastWriteTime(path); @@ -462,25 +241,6 @@ namespace System.IO { return File.GetLastWriteTimeUtc(path); } - public static void SetLastAccessTime(String path,DateTime lastAccessTime) - { - SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetLastAccessTimeUtc(String path,DateTime lastAccessTimeUtc) - { - using (SafeFileHandle handle = Directory.OpenHandle(path)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastAccessTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, null, &fileTime, null); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - public static DateTime GetLastAccessTime(String path) { return File.GetLastAccessTime(path); @@ -489,36 +249,13 @@ namespace System.IO { public static DateTime GetLastAccessTimeUtc(String path) { return File.GetLastAccessTimeUtc(path); - } - -#if FEATURE_MACL - public static DirectorySecurity GetAccessControl(String path) - { - return new DirectorySecurity(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); } - public static DirectorySecurity GetAccessControl(String path, AccessControlSections includeSections) - { - return new DirectorySecurity(path, includeSections); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public static void SetAccessControl(String path, DirectorySecurity directorySecurity) - { - if (directorySecurity == null) - throw new ArgumentNullException("directorySecurity"); - Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); - directorySecurity.Persist(fullPath); - } -#endif - // Returns an array of filenames in the DirectoryInfo specified by path public static String[] GetFiles(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -530,9 +267,9 @@ namespace System.IO { public static String[] GetFiles(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -544,11 +281,11 @@ namespace System.IO { public static String[] GetFiles(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -566,7 +303,6 @@ namespace System.IO { return InternalGetFileDirectoryNames(path, path, searchPattern, true, false, searchOption, true); } - [System.Security.SecurityCritical] internal static String[] UnsafeGetFiles(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); @@ -580,7 +316,7 @@ namespace System.IO { public static String[] GetDirectories(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -592,9 +328,9 @@ namespace System.IO { public static String[] GetDirectories(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -606,11 +342,11 @@ namespace System.IO { public static String[] GetDirectories(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -629,7 +365,6 @@ namespace System.IO { return InternalGetFileDirectoryNames(path, path, searchPattern, false, true, searchOption, true); } - [System.Security.SecurityCritical] internal static String[] UnsafeGetDirectories(String path, String searchPattern, SearchOption searchOption) { Contract.Requires(path != null); @@ -644,7 +379,7 @@ namespace System.IO { public static String[] GetFileSystemEntries(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -656,9 +391,9 @@ namespace System.IO { public static String[] GetFileSystemEntries(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -670,11 +405,11 @@ namespace System.IO { public static String[] GetFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result<String[]>() != null); Contract.EndContractBlock(); @@ -690,7 +425,6 @@ namespace System.IO { return InternalGetFileDirectoryNames(path, path, searchPattern, true, true, searchOption, true); } - // Private class that holds search data that is passed around // in the heap based stack recursion internal sealed class SearchData @@ -734,7 +468,7 @@ namespace System.IO { public static IEnumerable<String> EnumerateDirectories(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly); @@ -743,9 +477,9 @@ namespace System.IO { public static IEnumerable<String> EnumerateDirectories(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); @@ -754,11 +488,11 @@ namespace System.IO { public static IEnumerable<String> EnumerateDirectories(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalEnumerateDirectories(path, searchPattern, searchOption); @@ -776,7 +510,7 @@ namespace System.IO { public static IEnumerable<String> EnumerateFiles(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -786,9 +520,9 @@ namespace System.IO { public static IEnumerable<String> EnumerateFiles(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -798,11 +532,11 @@ namespace System.IO { public static IEnumerable<String> EnumerateFiles(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -822,7 +556,7 @@ namespace System.IO { public static IEnumerable<String> EnumerateFileSystemEntries(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -832,9 +566,9 @@ namespace System.IO { public static IEnumerable<String> EnumerateFileSystemEntries(String path, String searchPattern) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -844,11 +578,11 @@ namespace System.IO { public static IEnumerable<String> EnumerateFileSystemEntries(String path, String searchPattern, SearchOption searchOption) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.Ensures(Contract.Result<IEnumerable<String>>() != null); Contract.EndContractBlock(); @@ -882,7 +616,6 @@ namespace System.IO { // // Your application must have System Info permission. // - [System.Security.SecuritySafeCritical] // auto-generated public static String[] GetLogicalDrives() { Contract.Ensures(Contract.Result<String[]>() != null); @@ -914,29 +647,20 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] public static String GetDirectoryRoot(String path) { if (path==null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); - String root = fullPath.Substring(0, Path.GetRootLength(fullPath)); - String demandPath = GetDemandDir(root, true); - -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, path, demandPath); - state.EnsureState(); -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false); -#endif - + + string fullPath = Path.GetFullPath(path); + string root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath)); + return root; } internal static String InternalGetDirectoryRoot(String path) { if (path == null) return null; - return path.Substring(0, Path.GetRootLength(path)); + return path.Substring(0, PathInternal.GetRootLength(path)); } /*===============================CurrentDirectory=============================== @@ -946,77 +670,10 @@ namespace System.IO { **Arguments: The current DirectoryInfo to which to switch to the setter. **Exceptions: ==============================================================================*/ - [System.Security.SecuritySafeCritical] public static String GetCurrentDirectory() { - return InternalGetCurrentDirectory(true); - } - - [System.Security.SecurityCritical] - internal static String UnsafeGetCurrentDirectory() - { - return InternalGetCurrentDirectory(false); - } - - [System.Security.SecuritySafeCritical] - private static string InternalGetCurrentDirectory(bool checkHost) - { - string currentDirectory = ( -#if FEATURE_PATHCOMPAT - AppContextSwitches.UseLegacyPathHandling ? LegacyGetCurrentDirectory() : -#endif - NewGetCurrentDirectory()); - - string demandPath = GetDemandDir(currentDirectory, true); - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandPath, false, false); -#endif - return currentDirectory; - } - -#if FEATURE_PATHCOMPAT - [System.Security.SecurityCritical] - private static String LegacyGetCurrentDirectory() - { - StringBuilder sb = StringBuilderCache.Acquire(Path.MaxPath + 1); - if (Win32Native.GetCurrentDirectory(sb.Capacity, sb) == 0) - __Error.WinIOError(); - String currentDirectory = sb.ToString(); - // Note that if we have somehow put our command prompt into short - // file name mode (ie, by running edlin or a DOS grep, etc), then - // this will return a short file name. - if (currentDirectory.IndexOf('~') >= 0) { - int r = Win32Native.GetLongPathName(currentDirectory, sb, sb.Capacity); - if (r == 0 || r >= Path.MaxPath) { - int errorCode = Marshal.GetLastWin32Error(); - if (r >= Path.MaxPath) - errorCode = Win32Native.ERROR_FILENAME_EXCED_RANGE; - if (errorCode != Win32Native.ERROR_FILE_NOT_FOUND && - errorCode != Win32Native.ERROR_PATH_NOT_FOUND && - errorCode != Win32Native.ERROR_INVALID_FUNCTION && // by design - enough said. - errorCode != Win32Native.ERROR_ACCESS_DENIED) - __Error.WinIOError(errorCode, String.Empty); - } - currentDirectory = sb.ToString(); - } - StringBuilderCache.Release(sb); - String demandPath = GetDemandDir(currentDirectory, true); - - return currentDirectory; - } -#endif // FEATURE_PATHCOMPAT - - [System.Security.SecurityCritical] - private static string NewGetCurrentDirectory() - { - using (StringBuffer buffer = new StringBuffer(PathInternal.MaxShortPath)) + // Start with a buffer the size of MAX_PATH + using (StringBuffer buffer = new StringBuffer(260)) { uint result = 0; while ((result = Win32Native.GetCurrentDirectoryW(buffer.CharCapacity, buffer.GetHandle())) > buffer.CharCapacity) @@ -1033,35 +690,23 @@ namespace System.IO { #if !PLATFORM_UNIX if (buffer.Contains('~')) - return LongPathHelper.GetLongPathName(buffer); + return Path.GetFullPath(buffer.ToString()); #endif return buffer.ToString(); } } - #if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated - #else - [System.Security.SecuritySafeCritical] - #endif public static void SetCurrentDirectory(String path) { if (path==null) throw new ArgumentNullException("value"); if (path.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty")); - Contract.EndContractBlock(); if (path.Length >= Path.MaxPath) throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - // This will have some large effects on the rest of the runtime - // and other appdomains in this process. Demand unmanaged code. -#pragma warning disable 618 - new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); -#pragma warning restore 618 - String fulldestDirName = Path.GetFullPathInternal(path); + String fulldestDirName = Path.GetFullPath(path); if (!Win32Native.SetCurrentDirectory(fulldestDirName)) { // If path doesn't exist, this sets last error to 2 (File @@ -1074,52 +719,19 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] - public static void Move(String sourceDirName,String destDirName) { - InternalMove(sourceDirName, destDirName, true); - } - - [System.Security.SecurityCritical] - internal static void UnsafeMove(String sourceDirName,String destDirName) { - InternalMove(sourceDirName, destDirName, false); - } - - [System.Security.SecurityCritical] - private static void InternalMove(String sourceDirName,String destDirName,bool checkHost) { + public static void Move(String sourceDirName,String destDirName) + { if (sourceDirName==null) - throw new ArgumentNullException("sourceDirName"); + throw new ArgumentNullException(nameof(sourceDirName)); if (sourceDirName.Length==0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceDirName"); - + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(sourceDirName)); if (destDirName==null) - throw new ArgumentNullException("destDirName"); + throw new ArgumentNullException(nameof(destDirName)); if (destDirName.Length==0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destDirName"); - Contract.EndContractBlock(); - - String fullsourceDirName = Path.GetFullPathInternal(sourceDirName); - String sourcePath = GetDemandDir(fullsourceDirName, false); - - if (PathInternal.IsDirectoryTooLong(sourcePath)) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - String fulldestDirName = Path.GetFullPathInternal(destDirName); - String destPath = GetDemandDir(fulldestDirName, false); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destDirName)); - if (PathInternal.IsDirectoryTooLong(destPath)) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - -#if FEATURE_CORECLR - if (checkHost) { - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, sourceDirName, sourcePath); - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destDirName, destPath); - sourceState.EnsureState(); - destState.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, sourcePath, false, false); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, destPath, false, false); -#endif + String sourcePath = Path.GetFullPath(sourceDirName); + String destPath = Path.GetFullPath(destDirName); if (String.Compare(sourcePath, destPath, StringComparison.OrdinalIgnoreCase) == 0) throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustBeDifferent")); @@ -1135,7 +747,7 @@ namespace System.IO { if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // Source dir not found { hr = Win32Native.ERROR_PATH_NOT_FOUND; - __Error.WinIOError(hr, fullsourceDirName); + __Error.WinIOError(hr, sourcePath); } // This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons. if (hr == Win32Native.ERROR_ACCESS_DENIED) // WinNT throws IOException. This check is for Win9x. We can't change it for backcomp. @@ -1144,49 +756,22 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] public static void Delete(String path) { - String fullPath = Path.GetFullPathInternal(path); - Delete(fullPath, path, false, true); + String fullPath = Path.GetFullPath(path); + Delete(fullPath, path, false); } - [System.Security.SecuritySafeCritical] public static void Delete(String path, bool recursive) { - String fullPath = Path.GetFullPathInternal(path); - Delete(fullPath, path, recursive, true); + String fullPath = Path.GetFullPath(path); + Delete(fullPath, path, recursive); } - [System.Security.SecurityCritical] - internal static void UnsafeDelete(String path, bool recursive) - { - String fullPath = Path.GetFullPathInternal(path); - Delete(fullPath, path, recursive, false); - } - - // Called from DirectoryInfo as well. FullPath is fully qualified, + // Called from DirectoryInfo as well. FullPath is fully qualified, // while the user path is used for feedback in exceptions. - [System.Security.SecurityCritical] // auto-generated - internal static void Delete(String fullPath, String userPath, bool recursive, bool checkHost) + internal static void Delete(String fullPath, String userPath, bool recursive) { - String demandPath; - - // If not recursive, do permission check only on this directory - // else check for the whole directory structure rooted below - demandPath = GetDemandDir(fullPath, !recursive); - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, userPath, demandPath); - state.EnsureState(); - } -#else - // Make sure we have write permission to this directory - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false ).Demand(); -#endif - // Do not recursively delete through reparse points. Perhaps in a // future version we will add a new flag to control this behavior, // but for now we're much safer if we err on the conservative side. @@ -1206,10 +791,7 @@ namespace System.IO { DeleteHelper(fullPath, userPath, recursive, true); } - // Note that fullPath is fully qualified, while userPath may be - // relative. Use userPath for all exception messages to avoid leaking - // fully qualified path information. - [System.Security.SecurityCritical] // auto-generated + // Note that fullPath is fully qualified, while userPath may be relative. private static void DeleteHelper(String fullPath, String userPath, bool recursive, bool throwOnTopLevelDirectoryNotFound) { bool r; @@ -1229,12 +811,12 @@ namespace System.IO { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle - using (SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath+Path.DirectorySeparatorCharAsString+"*", data)) { + using (SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath + Path.DirectorySeparatorChar + "*", data)) { if (hnd.IsInvalid) { hr = Marshal.GetLastWin32Error(); __Error.WinIOError(hr, fullPath); } - + do { bool isDir = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); if (isDir) { @@ -1248,8 +830,8 @@ namespace System.IO { // itself. bool shouldRecurse = (0 == (data.dwFileAttributes & (int) FileAttributes.ReparsePoint)); if (shouldRecurse) { - String newFullPath = Path.InternalCombine(fullPath, data.cFileName); - String newUserPath = Path.InternalCombine(userPath, data.cFileName); + String newFullPath = Path.Combine(fullPath, data.cFileName); + String newUserPath = Path.Combine(userPath, data.cFileName); try { DeleteHelper(newFullPath, newUserPath, recursive, false); } @@ -1264,7 +846,7 @@ namespace System.IO { // unmount it. if (data.dwReserved0 == Win32Native.IO_REPARSE_TAG_MOUNT_POINT) { // Use full path plus a trailing '\' - String mountPoint = Path.InternalCombine(fullPath, data.cFileName + Path.DirectorySeparatorChar); + String mountPoint = Path.Combine(fullPath, data.cFileName + Path.DirectorySeparatorChar); r = Win32Native.DeleteVolumeMountPoint(mountPoint); if (!r) { hr = Marshal.GetLastWin32Error(); @@ -1283,7 +865,7 @@ namespace System.IO { // RemoveDirectory on a symbolic link will // remove the link itself. - String reparsePoint = Path.InternalCombine(fullPath, data.cFileName); + String reparsePoint = Path.Combine(fullPath, data.cFileName); r = Win32Native.RemoveDirectory(reparsePoint); if (!r) { hr = Marshal.GetLastWin32Error(); @@ -1301,7 +883,7 @@ namespace System.IO { } } else { - String fileName = Path.InternalCombine(fullPath, data.cFileName); + String fileName = Path.Combine(fullPath, data.cFileName); r = Win32Native.DeleteFile(fileName); if (!r) { hr = Marshal.GetLastWin32Error(); @@ -1346,44 +928,6 @@ namespace System.IO { __Error.WinIOError(hr, fullPath); } } - - // WinNT only. Win9x this code will not work. - [System.Security.SecurityCritical] // auto-generated - private static SafeFileHandle OpenHandle(String path) - { - String fullPath = Path.GetFullPathInternal(path); - String root = Path.GetPathRoot(fullPath); - if (root == fullPath && root[1] == Path.VolumeSeparatorChar) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIsVolume")); - -#if !FEATURE_CORECLR - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, GetDemandDir(fullPath, true), false, false); -#endif - - SafeFileHandle handle = Win32Native.SafeCreateFile ( - fullPath, - GENERIC_WRITE, - (FileShare) (FILE_SHARE_WRITE|FILE_SHARE_DELETE), - null, - FileMode.Open, - FILE_FLAG_BACKUP_SEMANTICS, - IntPtr.Zero - ); - - if (handle.IsInvalid) { - int hr = Marshal.GetLastWin32Error(); - __Error.WinIOError(hr, fullPath); - } - return handle; - } - - private const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; - private const int GENERIC_WRITE = unchecked((int)0x40000000); - private const int FILE_SHARE_WRITE = 0x00000002; - private const int FILE_SHARE_DELETE = 0x00000004; - private const int OPEN_EXISTING = 0x00000003; - private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; } - } diff --git a/src/mscorlib/src/System/IO/DirectoryInfo.cs b/src/mscorlib/src/System/IO/DirectoryInfo.cs index f7b0709e9e..c4c350d245 100644 --- a/src/mscorlib/src/System/IO/DirectoryInfo.cs +++ b/src/mscorlib/src/System/IO/DirectoryInfo.cs @@ -15,63 +15,33 @@ ** ===========================================================*/ -using System; -using System.Collections; using System.Collections.Generic; -using System.Security; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif -using System.Security.Permissions; using Microsoft.Win32; -using System.Text; using System.Runtime.InteropServices; -using System.Globalization; using System.Runtime.Serialization; using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.Contracts; -namespace System.IO { +namespace System.IO +{ [Serializable] [ComVisible(true)] - public sealed class DirectoryInfo : FileSystemInfo { - private String[] demandDir; - -#if FEATURE_CORECLR - // Migrating InheritanceDemands requires this default ctor, so we can annotate it. -#if FEATURE_CORESYSTEM - [System.Security.SecurityCritical] -#else - [System.Security.SecuritySafeCritical] -#endif //FEATURE_CORESYSTEM + public sealed class DirectoryInfo : FileSystemInfo + { + // Migrating InheritanceDemands requires this default ctor, so we can annotate it. private DirectoryInfo(){} - - [System.Security.SecurityCritical] - public static DirectoryInfo UnsafeCreateDirectoryInfo(String path) - { - if (path == null) - throw new ArgumentNullException("path"); - Contract.EndContractBlock(); - - DirectoryInfo di = new DirectoryInfo(); - di.Init(path, false); - return di; - } -#endif - - [System.Security.SecuritySafeCritical] public DirectoryInfo(String path) { if (path==null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); - Init(path, true); + Init(path); } - [System.Security.SecurityCritical] - private void Init(String path, bool checkHost) + private void Init(String path) { // Special case "<DriveLetter>:" to point to "<CurrentDirectory>" instead if ((path.Length == 2) && (path[1] == ':')) @@ -83,138 +53,79 @@ namespace System.IO { OriginalPath = path; } - // Must fully qualify the path for the security check - String fullPath = Path.GetFullPathInternal(path); - - demandDir = new String[] {Directory.GetDemandDir(fullPath, true)}; -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, OriginalPath, fullPath); - state.EnsureState(); - } -#else - new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false ).Demand(); -#endif - - FullPath = fullPath; + FullPath = Path.GetFullPath(path); ; DisplayPath = GetDisplayName(OriginalPath, FullPath); } -#if FEATURE_CORESYSTEM - [System.Security.SecuritySafeCritical] -#endif //FEATURE_CORESYSTEM internal DirectoryInfo(String fullPath, bool junk) { - Contract.Assert(Path.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!"); + Debug.Assert(PathInternal.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!"); // Fast path when we know a DirectoryInfo exists. OriginalPath = Path.GetFileName(fullPath); FullPath = fullPath; DisplayPath = GetDisplayName(OriginalPath, FullPath); - demandDir = new String[] {Directory.GetDemandDir(fullPath, true)}; } - [System.Security.SecurityCritical] // auto-generated private DirectoryInfo(SerializationInfo info, StreamingContext context) : base(info, context) { -#if !FEATURE_CORECLR - demandDir = new String[] {Directory.GetDemandDir(FullPath, true)}; - new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false ).Demand(); -#endif DisplayPath = GetDisplayName(OriginalPath, FullPath); } - public override String Name { + public override String Name + { get { -#if FEATURE_CORECLR // DisplayPath is dir name for coreclr return DisplayPath; -#else - // Return just dir name - return GetDirName(FullPath); -#endif } } public DirectoryInfo Parent { - [System.Security.SecuritySafeCritical] get { String parentName; // FullPath might be either "c:\bar" or "c:\bar\". Handle // those cases, as well as avoiding mangling "c:\". String s = FullPath; if (s.Length > 3 && s.EndsWith(Path.DirectorySeparatorChar)) - s = FullPath.Substring(0, FullPath.Length - 1); + s = FullPath.Substring(0, FullPath.Length - 1); parentName = Path.GetDirectoryName(s); if (parentName==null) return null; - DirectoryInfo dir = new DirectoryInfo(parentName,false); -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery | FileSecurityStateAccess.Read, String.Empty, dir.demandDir[0]); - state.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, dir.demandDir, false, false).Demand(); -#endif - return dir; + + return new DirectoryInfo(parentName, false); } } - -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] -#endif public DirectoryInfo CreateSubdirectory(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return CreateSubdirectory(path, null); } -#if FEATURE_MACL - [System.Security.SecuritySafeCritical] // auto-generated - public DirectoryInfo CreateSubdirectory(String path, DirectorySecurity directorySecurity) - { - return CreateSubdirectoryHelper(path, directorySecurity); - } -#else // FEATURE_MACL - #if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated - #endif public DirectoryInfo CreateSubdirectory(String path, Object directorySecurity) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return CreateSubdirectoryHelper(path, directorySecurity); } -#endif // FEATURE_MACL - [System.Security.SecurityCritical] // auto-generated private DirectoryInfo CreateSubdirectoryHelper(String path, Object directorySecurity) { Contract.Requires(path != null); - String newDirs = Path.InternalCombine(FullPath, path); - String fullPath = Path.GetFullPathInternal(newDirs); + String newDirs = Path.Combine(FullPath, path); + String fullPath = Path.GetFullPath(newDirs); if (0!=String.Compare(FullPath,0,fullPath,0, FullPath.Length,StringComparison.OrdinalIgnoreCase)) { String displayPath = __Error.GetDisplayablePath(DisplayPath, false); throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSubPath", path, displayPath)); } - // Ensure we have permission to create this subdirectory. - String demandDirForCreation = Directory.GetDemandDir(fullPath, true); -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, OriginalPath, demandDirForCreation); - state.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandDirForCreation }, false, false).Demand(); -#endif - Directory.InternalCreateDirectory(fullPath, path, directorySecurity); // Check for read permission to directory we hand back by calling this constructor. @@ -223,23 +134,11 @@ namespace System.IO { public void Create() { - Directory.InternalCreateDirectory(FullPath, OriginalPath, null, true); + Directory.InternalCreateDirectory(FullPath, OriginalPath, null); } -#if FEATURE_MACL - public void Create(DirectorySecurity directorySecurity) - { - Directory.InternalCreateDirectory(FullPath, OriginalPath, directorySecurity, true); - } -#endif - // Tests if the given path refers to an existing DirectoryInfo on disk. - // - // Your application must have Read permission to the directory's - // contents. - // public override bool Exists { - [System.Security.SecuritySafeCritical] // auto-generated get { try @@ -248,7 +147,6 @@ namespace System.IO { Refresh(); if (_dataInitialised != 0) // Refresh was unable to initialise the data return false; - return _data.fileAttributes != -1 && (_data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0; } catch @@ -257,30 +155,13 @@ namespace System.IO { } } } - -#if FEATURE_MACL - public DirectorySecurity GetAccessControl() - { - return Directory.GetAccessControl(FullPath, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); - } - - public DirectorySecurity GetAccessControl(AccessControlSections includeSections) - { - return Directory.GetAccessControl(FullPath, includeSections); - } - - public void SetAccessControl(DirectorySecurity directorySecurity) - { - Directory.SetAccessControl(FullPath, directorySecurity); - } -#endif // Returns an array of Files in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). public FileInfo[] GetFiles(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalGetFiles(searchPattern, SearchOption.TopDirectoryOnly); @@ -291,9 +172,9 @@ namespace System.IO { public FileInfo[] GetFiles(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalGetFiles(searchPattern, searchOption); @@ -328,7 +209,7 @@ namespace System.IO { public FileSystemInfo[] GetFileSystemInfos(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalGetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); @@ -339,9 +220,9 @@ namespace System.IO { public FileSystemInfo[] GetFileSystemInfos(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalGetFileSystemInfos(searchPattern, searchOption); @@ -372,7 +253,7 @@ namespace System.IO { public DirectoryInfo[] GetDirectories(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalGetDirectories(searchPattern, SearchOption.TopDirectoryOnly); @@ -384,9 +265,9 @@ namespace System.IO { public DirectoryInfo[] GetDirectories(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalGetDirectories(searchPattern, searchOption); @@ -413,7 +294,7 @@ namespace System.IO { public IEnumerable<DirectoryInfo> EnumerateDirectories(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalEnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); @@ -422,9 +303,9 @@ namespace System.IO { public IEnumerable<DirectoryInfo> EnumerateDirectories(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalEnumerateDirectories(searchPattern, searchOption); @@ -446,7 +327,7 @@ namespace System.IO { public IEnumerable<FileInfo> EnumerateFiles(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalEnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); @@ -455,9 +336,9 @@ namespace System.IO { public IEnumerable<FileInfo> EnumerateFiles(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalEnumerateFiles(searchPattern, searchOption); @@ -479,7 +360,7 @@ namespace System.IO { public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(String searchPattern) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); Contract.EndContractBlock(); return InternalEnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); @@ -488,9 +369,9 @@ namespace System.IO { public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(String searchPattern, SearchOption searchOption) { if (searchPattern == null) - throw new ArgumentNullException("searchPattern"); + throw new ArgumentNullException(nameof(searchPattern)); if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) - throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(searchOption), Environment.GetResourceString("ArgumentOutOfRange_Enum")); Contract.EndContractBlock(); return InternalEnumerateFileSystemInfos(searchPattern, searchOption); @@ -503,7 +384,7 @@ namespace System.IO { return FileSystemEnumerableFactory.CreateFileSystemInfoIterator(FullPath, OriginalPath, searchPattern, searchOption); } - + // Returns the root portion of the given path. The resulting string // consists of those rightmost characters of the path that constitute the // root of the path. Possible patterns for the resulting string are: An @@ -512,61 +393,27 @@ namespace System.IO { // where X is the drive letter), "X:\" (an absolute path on a given drive), // and "\\server\share" (a UNC path for a given server and share name). // The resulting string is null if path is null. - // - public DirectoryInfo Root { - [System.Security.SecuritySafeCritical] get { - String demandPath; - int rootLength = Path.GetRootLength(FullPath); + int rootLength = PathInternal.GetRootLength(FullPath); String rootPath = FullPath.Substring(0, rootLength); - demandPath = Directory.GetDemandDir(rootPath, true); - -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPath); - sourceState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { demandPath }, false, false).Demand(); -#endif + return new DirectoryInfo(rootPath); } } - [System.Security.SecuritySafeCritical] public void MoveTo(String destDirName) { if (destDirName==null) - throw new ArgumentNullException("destDirName"); + throw new ArgumentNullException(nameof(destDirName)); if (destDirName.Length==0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destDirName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destDirName)); Contract.EndContractBlock(); - -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, DisplayPath, Directory.GetDemandDir(FullPath, true)); - sourceState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandDir, false, false).Demand(); -#endif - String fullDestDirName = Path.GetFullPathInternal(destDirName); - String demandPath; + + String fullDestDirName = Path.GetFullPath(destDirName); if (!fullDestDirName.EndsWith(Path.DirectorySeparatorChar)) fullDestDirName = fullDestDirName + Path.DirectorySeparatorChar; - demandPath = fullDestDirName + '.'; - - // Demand read & write permission to destination. The reason is - // we hand back a DirectoryInfo to the destination that would allow - // you to read a directory listing from that directory. Sure, you - // had the ability to read the file contents in the old location, - // but you technically also need read permissions to the new - // location as well, and write is not a true superset of read. -#if FEATURE_CORECLR - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destDirName, demandPath); - destState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandPath).Demand(); -#endif - String fullSourcePath; if (FullPath.EndsWith(Path.DirectorySeparatorChar)) fullSourcePath = FullPath; @@ -599,22 +446,19 @@ namespace System.IO { FullPath = fullDestDirName; OriginalPath = destDirName; DisplayPath = GetDisplayName(OriginalPath, FullPath); - demandDir = new String[] { Directory.GetDemandDir(FullPath, true) }; // Flush any cached information about the directory. _dataInitialised = -1; } - [System.Security.SecuritySafeCritical] public override void Delete() { - Directory.Delete(FullPath, OriginalPath, false, true); + Directory.Delete(FullPath, OriginalPath, false); } - [System.Security.SecuritySafeCritical] public void Delete(bool recursive) { - Directory.Delete(FullPath, OriginalPath, recursive, true); + Directory.Delete(FullPath, OriginalPath, recursive); } // Returns the fully qualified path @@ -625,8 +469,8 @@ namespace System.IO { private static String GetDisplayName(String originalPath, String fullPath) { - Contract.Assert(originalPath != null); - Contract.Assert(fullPath != null); + Debug.Assert(originalPath != null); + Debug.Assert(fullPath != null); String displayName = ""; @@ -637,18 +481,14 @@ namespace System.IO { } else { -#if FEATURE_CORECLR displayName = GetDirName(fullPath); -#else - displayName = originalPath; -#endif } return displayName; } private static String GetDirName(String fullPath) { - Contract.Assert(fullPath != null); + Debug.Assert(fullPath != null); String dirName = null; if (fullPath.Length > 3) @@ -666,7 +506,6 @@ namespace System.IO { } return dirName; } - - } + } } diff --git a/src/mscorlib/src/System/IO/DriveInfo.cs b/src/mscorlib/src/System/IO/DriveInfo.cs deleted file mode 100644 index be75e8979d..0000000000 --- a/src/mscorlib/src/System/IO/DriveInfo.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** -** Purpose: Exposes routines for exploring a drive. -** -** -===========================================================*/ - -using System; -using System.Text; -using System.Runtime.InteropServices; -using Microsoft.Win32; -using System.Security.Permissions; -using System.Runtime.Serialization; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; - -namespace System.IO -{ - // Matches Win32's DRIVE_XXX #defines from winbase.h - [Serializable] -[System.Runtime.InteropServices.ComVisible(true)] - public enum DriveType - { - Unknown = 0, - NoRootDirectory = 1, - Removable = 2, - Fixed = 3, - Network = 4, - CDRom = 5, - Ram = 6 - } - - // Ideally we'll get a better security permission, but possibly - // not for Whidbey. -#if FEATURE_SERIALIZATION - [Serializable] -#endif - [ComVisible(true)] - public sealed class DriveInfo -#if FEATURE_SERIALIZATION - : ISerializable -#endif - { - private String _name; - - private const String NameField = "_name"; // For serialization - - [System.Security.SecuritySafeCritical] // auto-generated - public DriveInfo(String driveName) - { - if (driveName == null) - throw new ArgumentNullException("driveName"); - Contract.EndContractBlock(); - if (driveName.Length == 1) - _name = driveName + ":\\"; - else { - // GetPathRoot does not check all invalid characters - Path.CheckInvalidPathChars(driveName); - _name = Path.GetPathRoot(driveName); - // Disallow null or empty drive letters and UNC paths - if (_name == null || _name.Length == 0 || _name.StartsWith("\\\\", StringComparison.Ordinal)) - throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDriveLetterOrRootDir")); - } - // We want to normalize to have a trailing backslash so we don't have two equivalent forms and - // because some Win32 API don't work without it. - if (_name.Length == 2 && _name[1] == ':') { - _name = _name + "\\"; - } - - // Now verify that the drive letter could be a real drive name. - // On Windows this means it's between A and Z, ignoring case. - // On a Unix platform, perhaps this should be a device name with - // a partition like /dev/hdc0, or possibly a mount point. - char letter = driveName[0]; - if (!((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'))) - throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDriveLetterOrRootDir")); - - // Now do a security check. - String demandPath = _name + '.'; - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPath).Demand(); - } - - [System.Security.SecurityCritical] // auto-generated - private DriveInfo(SerializationInfo info, StreamingContext context) - { - // Need to add in a security check here once it has been spec'ed. - _name = (String) info.GetValue(NameField, typeof(String)); - - // Now do a security check. - String demandPath = _name + '.'; - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPath).Demand(); - } - - public String Name { - get { return _name; } - } - - public DriveType DriveType { - [System.Security.SecuritySafeCritical] // auto-generated - get { - // GetDriveType can't fail - return (DriveType) Win32Native.GetDriveType(Name); - } - } - - public String DriveFormat { - [System.Security.SecuritySafeCritical] // auto-generated - get { - const int volNameLen = 50; - StringBuilder volumeName = new StringBuilder(volNameLen); - const int fileSystemNameLen = 50; - StringBuilder fileSystemName = new StringBuilder(fileSystemNameLen); - int serialNumber, maxFileNameLen, fileSystemFlags; - - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.GetVolumeInformation(Name, volumeName, volNameLen, out serialNumber, out maxFileNameLen, out fileSystemFlags, fileSystemName, fileSystemNameLen); - if (!r) { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIODriveError(Name, errorCode); - } - } - finally { - Win32Native.SetErrorMode(oldMode); - } - return fileSystemName.ToString(); - } - } - - public bool IsReady { - [System.Security.SecuritySafeCritical] // auto-generated - get { - return Directory.InternalExists(Name); - } - } - - public long AvailableFreeSpace { - [System.Security.SecuritySafeCritical] // auto-generated - get { - long userBytes, totalBytes, freeBytes; - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); - if (!r) - __Error.WinIODriveError(Name); - } - finally { - Win32Native.SetErrorMode(oldMode); - } - return userBytes; - } - } - - public long TotalFreeSpace { - [System.Security.SecuritySafeCritical] // auto-generated - get { - long userBytes, totalBytes, freeBytes; - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); - if (!r) - __Error.WinIODriveError(Name); - } - finally { - Win32Native.SetErrorMode(oldMode); - } - return freeBytes; - } - } - - public long TotalSize { - [System.Security.SecuritySafeCritical] // auto-generated - get { - // Don't cache this, to handle variable sized floppy drives - // or other various removable media drives. - long userBytes, totalBytes, freeBytes; - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); - if (!r) - __Error.WinIODriveError(Name); - } - finally { - Win32Native.SetErrorMode(oldMode); - } - return totalBytes; - } - } - - public static DriveInfo[] GetDrives() - { - // Directory.GetLogicalDrives demands unmanaged code permission - String[] drives = Directory.GetLogicalDrives(); - DriveInfo[] di = new DriveInfo[drives.Length]; - for(int i=0; i<drives.Length; i++) - di[i] = new DriveInfo(drives[i]); - return di; - } - - public DirectoryInfo RootDirectory { - get { - return new DirectoryInfo(Name); - } - } - - // Null is a valid volume label. - public String VolumeLabel { - [System.Security.SecuritySafeCritical] // auto-generated - get { - // NTFS uses a limit of 32 characters for the volume label, - // as of Windows Server 2003. - const int volNameLen = 50; - StringBuilder volumeName = new StringBuilder(volNameLen); - const int fileSystemNameLen = 50; - StringBuilder fileSystemName = new StringBuilder(fileSystemNameLen); - int serialNumber, maxFileNameLen, fileSystemFlags; - - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.GetVolumeInformation(Name, volumeName, volNameLen, out serialNumber, out maxFileNameLen, out fileSystemFlags, fileSystemName, fileSystemNameLen); - if (!r) { - int errorCode = Marshal.GetLastWin32Error(); - // Win9x appears to return ERROR_INVALID_DATA when a - // drive doesn't exist. - if (errorCode == Win32Native.ERROR_INVALID_DATA) - errorCode = Win32Native.ERROR_INVALID_DRIVE; - __Error.WinIODriveError(Name, errorCode); - } - } - finally { - Win32Native.SetErrorMode(oldMode); - } - return volumeName.ToString(); - } - [System.Security.SecuritySafeCritical] // auto-generated - set { - String demandPath = _name + '.'; - new FileIOPermission(FileIOPermissionAccess.Write, demandPath).Demand(); - - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - bool r = Win32Native.SetVolumeLabel(Name, value); - if (!r) { - int errorCode = Marshal.GetLastWin32Error(); - // Provide better message - if (errorCode == Win32Native.ERROR_ACCESS_DENIED) - throw new UnauthorizedAccessException(Environment.GetResourceString("InvalidOperation_SetVolumeLabelFailed")); - __Error.WinIODriveError(Name, errorCode); - } - } - finally { - Win32Native.SetErrorMode(oldMode); - } - } - } - - public override String ToString() - { - return Name; - } - -#if FEATURE_SERIALIZATION - /// <internalonly/> - [System.Security.SecurityCritical] - void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) - { - // No need for an additional security check - everything is public. - info.AddValue(NameField, _name, typeof(String)); - } -#endif - - } -} diff --git a/src/mscorlib/src/System/IO/EncodingCache.cs b/src/mscorlib/src/System/IO/EncodingCache.cs new file mode 100644 index 0000000000..53379bc77f --- /dev/null +++ b/src/mscorlib/src/System/IO/EncodingCache.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text; + +namespace System.IO +{ + internal static class EncodingCache + { + internal static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + } +} diff --git a/src/mscorlib/src/System/IO/File.cs b/src/mscorlib/src/System/IO/File.cs index cfcb469bc3..9f89f81a91 100644 --- a/src/mscorlib/src/System/IO/File.cs +++ b/src/mscorlib/src/System/IO/File.cs @@ -14,34 +14,32 @@ ** ===========================================================*/ -using System; using System.Security.Permissions; -using PermissionSet = System.Security.PermissionSet; using Win32Native = Microsoft.Win32.Win32Native; using System.Runtime.InteropServices; using System.Security; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif using System.Text; using Microsoft.Win32.SafeHandles; using System.Collections.Generic; -using System.Globalization; -using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.Contracts; - -namespace System.IO { + +namespace System.IO +{ // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. [ComVisible(true)] public static class File { + private const int ERROR_INVALID_PARAMETER = 87; + internal const int GENERIC_READ = unchecked((int)0x80000000); + private const int GetFileExInfoStandard = 0; public static StreamReader OpenText(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return new StreamReader(path); } @@ -49,7 +47,7 @@ namespace System.IO { public static StreamWriter CreateText(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return new StreamWriter(path,false); } @@ -57,7 +55,7 @@ namespace System.IO { public static StreamWriter AppendText(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); return new StreamWriter(path,true); } @@ -67,88 +65,51 @@ namespace System.IO { // destination file already exists. Use the // Copy(String, String, boolean) method to allow // overwriting an existing file. - // - // The caller must have certain FileIOPermissions. The caller must have - // Read permission to sourceFileName and Create - // and Write permissions to destFileName. - // public static void Copy(String sourceFileName, String destFileName) { if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(sourceFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(destFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (sourceFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(sourceFileName)); if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - InternalCopy(sourceFileName, destFileName, false, true); + InternalCopy(sourceFileName, destFileName, false); } // Copies an existing file to a new file. If overwrite is // false, then an IOException is thrown if the destination file // already exists. If overwrite is true, the file is // overwritten. - // - // The caller must have certain FileIOPermissions. The caller must have - // Read permission to sourceFileName - // and Write permissions to destFileName. - // public static void Copy(String sourceFileName, String destFileName, bool overwrite) { if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(sourceFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(destFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (sourceFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(sourceFileName)); if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - InternalCopy(sourceFileName, destFileName, overwrite, true); - } - - [System.Security.SecurityCritical] - internal static void UnsafeCopy(String sourceFileName, String destFileName, bool overwrite) { - if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName", Environment.GetResourceString("ArgumentNull_FileName")); - if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); - if (sourceFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceFileName"); - if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); - Contract.EndContractBlock(); - - InternalCopy(sourceFileName, destFileName, overwrite, false); + InternalCopy(sourceFileName, destFileName, overwrite); } /// <devdoc> /// Note: This returns the fully qualified name of the destination file. /// </devdoc> - [System.Security.SecuritySafeCritical] - internal static String InternalCopy(String sourceFileName, String destFileName, bool overwrite, bool checkHost) { + internal static String InternalCopy(String sourceFileName, String destFileName, bool overwrite) + { Contract.Requires(sourceFileName != null); Contract.Requires(destFileName != null); Contract.Requires(sourceFileName.Length > 0); Contract.Requires(destFileName.Length > 0); - String fullSourceFileName = Path.GetFullPathInternal(sourceFileName); - String fullDestFileName = Path.GetFullPathInternal(destFileName); - -#if FEATURE_CORECLR - if (checkHost) { - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, sourceFileName, fullSourceFileName); - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destFileName, fullDestFileName); - sourceState.EnsureState(); - destState.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullSourceFileName, false, false); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); -#endif - + String fullSourceFileName = Path.GetFullPath(sourceFileName); + String fullDestFileName = Path.GetFullPath(destFileName); + bool r = Win32Native.CopyFile(fullSourceFileName, fullDestFileName, !overwrite); if (!r) { // Save Win32 error because subsequent checks will overwrite this HRESULT. @@ -156,14 +117,6 @@ namespace System.IO { String fileName = destFileName; if (errorCode != Win32Native.ERROR_FILE_EXISTS) { - // For a number of error codes (sharing violation, path - // not found, etc) we don't know if the problem was with - // the source or dest file. Try reading the source file. - using(SafeFileHandle handle = Win32Native.UnsafeCreateFile(fullSourceFileName, FileStream.GENERIC_READ, FileShare.Read, null, FileMode.Open, 0, IntPtr.Zero)) { - if (handle.IsInvalid) - fileName = sourceFileName; - } - if (errorCode == Win32Native.ERROR_ACCESS_DENIED) { if (Directory.InternalExists(fullDestFileName)) throw new IOException(Environment.GetResourceString("Arg_FileIsDirectory_Name", destFileName), Win32Native.ERROR_ACCESS_DENIED, fullDestFileName); @@ -172,19 +125,14 @@ namespace System.IO { __Error.WinIOError(errorCode, fileName); } - + return fullDestFileName; } - // Creates a file in a particular path. If the file exists, it is replaced. // The file is opened with ReadWrite accessand cannot be opened by another // application until it has been closed. An IOException is thrown if the // directory specified doesn't exist. - // - // Your application must have Create, Read, and Write permissions to - // the file. - // public static FileStream Create(String path) { return Create(path, FileStream.DefaultBufferSize); } @@ -193,10 +141,6 @@ namespace System.IO { // The file is opened with ReadWrite access and cannot be opened by another // application until it has been closed. An IOException is thrown if the // directory specified doesn't exist. - // - // Your application must have Create, Read, and Write permissions to - // the file. - // public static FileStream Create(String path, int bufferSize) { return new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize); } @@ -206,57 +150,23 @@ namespace System.IO { FileShare.None, bufferSize, options); } -#if FEATURE_MACL - public static FileStream Create(String path, int bufferSize, FileOptions options, FileSecurity fileSecurity) { - return new FileStream(path, FileMode.Create, FileSystemRights.Read | FileSystemRights.Write, - FileShare.None, bufferSize, options, fileSecurity); - } -#endif - // Deletes a file. The file specified by the designated path is deleted. // If the file does not exist, Delete succeeds without throwing // an exception. // // On NT, Delete will fail for a file that is open for normal I/O // or a file that is memory mapped. - // - // Your application must have Delete permission to the target file. - // - [System.Security.SecuritySafeCritical] public static void Delete(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); Contract.EndContractBlock(); - - InternalDelete(path, true); - } - [System.Security.SecurityCritical] - internal static void UnsafeDelete(String path) - { - if (path == null) - throw new ArgumentNullException("path"); - Contract.EndContractBlock(); - - InternalDelete(path, false); + InternalDelete(path); } - [System.Security.SecurityCritical] - internal static void InternalDelete(String path, bool checkHost) + internal static void InternalDelete(String path) { - String fullPath = Path.GetFullPathInternal(path); - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, path, fullPath); - state.EnsureState(); - } -#else - // For security check, path should be resolved to an absolute path. - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullPath, false, false); - -#endif + String fullPath = Path.GetFullPath(path); bool r = Win32Native.DeleteFile(fullPath); if (!r) { int hr = Marshal.GetLastWin32Error(); @@ -267,76 +177,16 @@ namespace System.IO { } } - - [System.Security.SecuritySafeCritical] // auto-generated - public static void Decrypt(String path) - { - if (path == null) - throw new ArgumentNullException("path"); - Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, fullPath, false, false); - - bool r = Win32Native.DecryptFile(fullPath, 0); - if (!r) { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == Win32Native.ERROR_ACCESS_DENIED) { - // Check to see if the file system is not NTFS. If so, - // throw a different exception. - DriveInfo di = new DriveInfo(Path.GetPathRoot(fullPath)); - if (!String.Equals("NTFS", di.DriveFormat)) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_EncryptionNeedsNTFS")); - } - __Error.WinIOError(errorCode, fullPath); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public static void Encrypt(String path) - { - if (path == null) - throw new ArgumentNullException("path"); - Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, fullPath, false, false); - - bool r = Win32Native.EncryptFile(fullPath); - if (!r) { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == Win32Native.ERROR_ACCESS_DENIED) { - // Check to see if the file system is not NTFS. If so, - // throw a different exception. - DriveInfo di = new DriveInfo(Path.GetPathRoot(fullPath)); - if (!String.Equals("NTFS", di.DriveFormat)) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_EncryptionNeedsNTFS")); - } - __Error.WinIOError(errorCode, fullPath); - } - } - // Tests if a file exists. The result is true if the file // given by the specified path exists; otherwise, the result is // false. Note that if path describes a directory, // Exists will return true. - // - // Your application must have Read permission for the target directory. - // - [System.Security.SecuritySafeCritical] public static bool Exists(String path) { - return InternalExistsHelper(path, true); + return InternalExistsHelper(path); } - [System.Security.SecurityCritical] - internal static bool UnsafeExists(String path) - { - return InternalExistsHelper(path, false); - } - - [System.Security.SecurityCritical] - private static bool InternalExistsHelper(String path, bool checkHost) + private static bool InternalExistsHelper(String path) { try { @@ -345,26 +195,17 @@ namespace System.IO { if (path.Length == 0) return false; - path = Path.GetFullPathInternal(path); + path = Path.GetFullPath(path); + // After normalizing, check whether path ends in directory separator. // Otherwise, FillAttributeInfo removes it and we may return a false positive. - // GetFullPathInternal should never return null - Contract.Assert(path != null, "File.Exists: GetFullPathInternal returned null"); - if (path.Length > 0 && Path.IsDirectorySeparator(path[path.Length - 1])) + // GetFullPath should never return null + Debug.Assert(path != null, "File.Exists: GetFullPath returned null"); + if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1])) { return false; } -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, path); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, path, false, false); -#endif - return InternalExists(path); } catch (ArgumentException) { } @@ -376,7 +217,6 @@ namespace System.IO { return false; } - [System.Security.SecurityCritical] // auto-generated internal static bool InternalExists(String path) { Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(path, ref data, false, true); @@ -397,51 +237,19 @@ namespace System.IO { return new FileStream(path, mode, access, share); } - public static void SetCreationTime(String path, DateTime creationTime) - { - SetCreationTimeUtc(path, creationTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetCreationTimeUtc(String path, DateTime creationTimeUtc) - { - SafeFileHandle handle; - using(OpenFile(path, FileAccess.Write, out handle)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(creationTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, &fileTime, null, null); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - - [System.Security.SecuritySafeCritical] public static DateTime GetCreationTime(String path) { - return InternalGetCreationTimeUtc(path, true).ToLocalTime(); + return InternalGetCreationTimeUtc(path).ToLocalTime(); } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetCreationTimeUtc(String path) { - return InternalGetCreationTimeUtc(path, false); // this API isn't exposed in Silverlight + return InternalGetCreationTimeUtc(path); // this API isn't exposed in Silverlight } - [System.Security.SecurityCritical] - private static DateTime InternalGetCreationTimeUtc(String path, bool checkHost) + private static DateTime InternalGetCreationTimeUtc(String path) { - String fullPath = Path.GetFullPathInternal(path); -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, fullPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); -#endif + String fullPath = Path.GetFullPath(path); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(fullPath, ref data, false, false); @@ -452,51 +260,19 @@ namespace System.IO { return DateTime.FromFileTimeUtc(dt); } - public static void SetLastAccessTime(String path, DateTime lastAccessTime) - { - SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetLastAccessTimeUtc(String path, DateTime lastAccessTimeUtc) - { - SafeFileHandle handle; - using(OpenFile(path, FileAccess.Write, out handle)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastAccessTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, null, &fileTime, null); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - - [System.Security.SecuritySafeCritical] public static DateTime GetLastAccessTime(String path) { - return InternalGetLastAccessTimeUtc(path, true).ToLocalTime(); + return InternalGetLastAccessTimeUtc(path).ToLocalTime(); } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetLastAccessTimeUtc(String path) { - return InternalGetLastAccessTimeUtc(path, false); // this API isn't exposed in Silverlight + return InternalGetLastAccessTimeUtc(path); } - [System.Security.SecurityCritical] - private static DateTime InternalGetLastAccessTimeUtc(String path, bool checkHost) - { - String fullPath = Path.GetFullPathInternal(path); -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, fullPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); -#endif + private static DateTime InternalGetLastAccessTimeUtc(String path) + { + String fullPath = Path.GetFullPath(path); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(fullPath, ref data, false, false); @@ -507,51 +283,19 @@ namespace System.IO { return DateTime.FromFileTimeUtc(dt); } - public static void SetLastWriteTime(String path, DateTime lastWriteTime) - { - SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public unsafe static void SetLastWriteTimeUtc(String path, DateTime lastWriteTimeUtc) - { - SafeFileHandle handle; - using(OpenFile(path, FileAccess.Write, out handle)) { - Win32Native.FILE_TIME fileTime = new Win32Native.FILE_TIME(lastWriteTimeUtc.ToFileTimeUtc()); - bool r = Win32Native.SetFileTime(handle, null, null, &fileTime); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - __Error.WinIOError(errorCode, path); - } - } - } - - [System.Security.SecuritySafeCritical] public static DateTime GetLastWriteTime(String path) { - return InternalGetLastWriteTimeUtc(path, true).ToLocalTime(); + return InternalGetLastWriteTimeUtc(path).ToLocalTime(); } - [System.Security.SecuritySafeCritical] // auto-generated public static DateTime GetLastWriteTimeUtc(String path) { - return InternalGetLastWriteTimeUtc(path, false); // this API isn't exposed in Silverlight + return InternalGetLastWriteTimeUtc(path); } - [System.Security.SecurityCritical] - private static DateTime InternalGetLastWriteTimeUtc(String path, bool checkHost) + private static DateTime InternalGetLastWriteTimeUtc(String path) { - String fullPath = Path.GetFullPathInternal(path); -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, fullPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); -#endif + String fullPath = Path.GetFullPath(path); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(fullPath, ref data, false, false); @@ -562,16 +306,9 @@ namespace System.IO { return DateTime.FromFileTimeUtc(dt); } - [System.Security.SecuritySafeCritical] public static FileAttributes GetAttributes(String path) { - String fullPath = Path.GetFullPathInternal(path); -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, path, fullPath); - state.EnsureState(); -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); -#endif + String fullPath = Path.GetFullPath(path); Win32Native.WIN32_FILE_ATTRIBUTE_DATA data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); int dataInitialised = FillAttributeInfo(fullPath, ref data, false, true); @@ -581,17 +318,9 @@ namespace System.IO { return (FileAttributes) data.fileAttributes; } - #if FEATURE_CORECLR - [System.Security.SecurityCritical] - #else - [System.Security.SecuritySafeCritical] - #endif public static void SetAttributes(String path, FileAttributes fileAttributes) { - String fullPath = Path.GetFullPathInternal(path); -#if !FEATURE_CORECLR - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullPath, false, false); -#endif + String fullPath = Path.GetFullPath(path); bool r = Win32Native.SetFileAttributes(fullPath, (int) fileAttributes); if (!r) { int hr = Marshal.GetLastWin32Error(); @@ -601,158 +330,88 @@ namespace System.IO { } } -#if FEATURE_MACL - public static FileSecurity GetAccessControl(String path) - { - return GetAccessControl(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); - } - - public static FileSecurity GetAccessControl(String path, AccessControlSections includeSections) - { - // Appropriate security check should be done for us by FileSecurity. - return new FileSecurity(path, includeSections); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public static void SetAccessControl(String path, FileSecurity fileSecurity) - { - if (fileSecurity == null) - throw new ArgumentNullException("fileSecurity"); - Contract.EndContractBlock(); - - String fullPath = Path.GetFullPathInternal(path); - // Appropriate security check should be done for us by FileSecurity. - fileSecurity.Persist(fullPath); - } -#endif - public static FileStream OpenRead(String path) { return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); } - public static FileStream OpenWrite(String path) { return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); } - [System.Security.SecuritySafeCritical] // auto-generated public static String ReadAllText(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); - return InternalReadAllText(path, Encoding.UTF8, true); + return InternalReadAllText(path, Encoding.UTF8); } - [System.Security.SecuritySafeCritical] // auto-generated public static String ReadAllText(String path, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - Contract.EndContractBlock(); - - return InternalReadAllText(path, encoding, true); - } - - [System.Security.SecurityCritical] - internal static String UnsafeReadAllText(String path) - { - if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); - return InternalReadAllText(path, Encoding.UTF8, false); + return InternalReadAllText(path, encoding); } - [System.Security.SecurityCritical] - private static String InternalReadAllText(String path, Encoding encoding, bool checkHost) + private static String InternalReadAllText(String path, Encoding encoding) { Contract.Requires(path != null); Contract.Requires(encoding != null); Contract.Requires(path.Length > 0); - using (StreamReader sr = new StreamReader(path, encoding, true, StreamReader.DefaultBufferSize, checkHost)) + using (StreamReader sr = new StreamReader(path, encoding, true, StreamReader.DefaultBufferSize)) return sr.ReadToEnd(); } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllText(String path, String contents) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); - InternalWriteAllText(path, contents, StreamWriter.UTF8NoBOM, true); + InternalWriteAllText(path, contents, StreamWriter.UTF8NoBOM); } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllText(String path, String contents, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); - InternalWriteAllText(path, contents, encoding, true); + InternalWriteAllText(path, contents, encoding); } - - [System.Security.SecurityCritical] - internal static void UnsafeWriteAllText(String path, String contents) - { - if (path == null) - throw new ArgumentNullException("path"); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - Contract.EndContractBlock(); - InternalWriteAllText(path, contents, StreamWriter.UTF8NoBOM, false); - } - - [System.Security.SecurityCritical] - private static void InternalWriteAllText(String path, String contents, Encoding encoding, bool checkHost) + private static void InternalWriteAllText(String path, String contents, Encoding encoding) { Contract.Requires(path != null); Contract.Requires(encoding != null); Contract.Requires(path.Length > 0); - using (StreamWriter sw = new StreamWriter(path, false, encoding, StreamWriter.DefaultBufferSize, checkHost)) + using (StreamWriter sw = new StreamWriter(path, false, encoding, StreamWriter.DefaultBufferSize)) sw.Write(contents); } - [System.Security.SecuritySafeCritical] // auto-generated public static byte[] ReadAllBytes(String path) { - return InternalReadAllBytes(path, true); - } - - [System.Security.SecurityCritical] - internal static byte[] UnsafeReadAllBytes(String path) - { - return InternalReadAllBytes(path, false); - } - - - [System.Security.SecurityCritical] - private static byte[] InternalReadAllBytes(String path, bool checkHost) - { byte[] bytes; using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, - FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, checkHost)) { + FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false)) { // Do a blocking read int index = 0; long fileLength = fs.Length; @@ -771,43 +430,27 @@ namespace System.IO { return bytes; } - [System.Security.SecuritySafeCritical] // auto-generated public static void WriteAllBytes(String path, byte[] bytes) { if (path == null) - throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); + throw new ArgumentNullException(nameof(path), Environment.GetResourceString("ArgumentNull_Path")); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); if (bytes == null) - throw new ArgumentNullException("bytes"); + throw new ArgumentNullException(nameof(bytes)); Contract.EndContractBlock(); - InternalWriteAllBytes(path, bytes, true); + InternalWriteAllBytes(path, bytes); } - [System.Security.SecurityCritical] - internal static void UnsafeWriteAllBytes(String path, byte[] bytes) - { - if (path == null) - throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - if (bytes == null) - throw new ArgumentNullException("bytes"); - Contract.EndContractBlock(); - - InternalWriteAllBytes(path, bytes, false); - } - - [System.Security.SecurityCritical] - private static void InternalWriteAllBytes(String path, byte[] bytes, bool checkHost) + private static void InternalWriteAllBytes(String path, byte[] bytes) { Contract.Requires(path != null); Contract.Requires(path.Length != 0); Contract.Requires(bytes != null); using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, - FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, checkHost)) + FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false)) { fs.Write(bytes, 0, bytes.Length); } @@ -816,7 +459,7 @@ namespace System.IO { public static String[] ReadAllLines(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -827,9 +470,9 @@ namespace System.IO { public static String[] ReadAllLines(String path, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -856,9 +499,9 @@ namespace System.IO { public static IEnumerable<String> ReadLines(String path) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), "path"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), nameof(path)); Contract.EndContractBlock(); return ReadLinesIterator.CreateIterator(path, Encoding.UTF8); @@ -867,11 +510,11 @@ namespace System.IO { public static IEnumerable<String> ReadLines(String path, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), "path"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath"), nameof(path)); Contract.EndContractBlock(); return ReadLinesIterator.CreateIterator(path, encoding); @@ -880,9 +523,9 @@ namespace System.IO { public static void WriteAllLines(String path, String[] contents) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -893,11 +536,11 @@ namespace System.IO { public static void WriteAllLines(String path, String[] contents, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -908,9 +551,9 @@ namespace System.IO { public static void WriteAllLines(String path, IEnumerable<String> contents) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -921,11 +564,11 @@ namespace System.IO { public static void WriteAllLines(String path, IEnumerable<String> contents, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -950,7 +593,7 @@ namespace System.IO { public static void AppendAllText(String path, String contents) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -961,9 +604,9 @@ namespace System.IO { public static void AppendAllText(String path, String contents, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -984,9 +627,9 @@ namespace System.IO { public static void AppendAllLines(String path, IEnumerable<String> contents) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -997,11 +640,11 @@ namespace System.IO { public static void AppendAllLines(String path, IEnumerable<String> contents, Encoding encoding) { if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (contents == null) - throw new ArgumentNullException("contents"); + throw new ArgumentNullException(nameof(contents)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); Contract.EndContractBlock(); @@ -1011,48 +654,23 @@ namespace System.IO { // Moves a specified file to a new location and potentially a new file name. // This method does work across volumes. - // - // The caller must have certain FileIOPermissions. The caller must - // have Read and Write permission to - // sourceFileName and Write - // permissions to destFileName. - // - [System.Security.SecuritySafeCritical] public static void Move(String sourceFileName, String destFileName) { - InternalMove(sourceFileName, destFileName, true); - } - - [System.Security.SecurityCritical] - internal static void UnsafeMove(String sourceFileName, String destFileName) { - InternalMove(sourceFileName, destFileName, false); + InternalMove(sourceFileName, destFileName); } - [System.Security.SecurityCritical] - private static void InternalMove(String sourceFileName, String destFileName, bool checkHost) { + private static void InternalMove(String sourceFileName, String destFileName) { if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(sourceFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(destFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (sourceFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "sourceFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(sourceFileName)); if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - String fullSourceFileName = Path.GetFullPathInternal(sourceFileName); - String fullDestFileName = Path.GetFullPathInternal(destFileName); - -#if FEATURE_CORECLR - if (checkHost) { - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, sourceFileName, fullSourceFileName); - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destFileName, fullDestFileName); - sourceState.EnsureState(); - destState.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, fullSourceFileName, false, false); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); -#endif + String fullSourceFileName = Path.GetFullPath(sourceFileName); + String fullDestFileName = Path.GetFullPath(destFileName); if (!InternalExists(fullSourceFileName)) __Error.WinIOError(Win32Native.ERROR_FILE_NOT_FOUND, fullSourceFileName); @@ -1067,9 +685,9 @@ namespace System.IO { public static void Replace(String sourceFileName, String destinationFileName, String destinationBackupFileName) { if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName"); + throw new ArgumentNullException(nameof(sourceFileName)); if (destinationFileName == null) - throw new ArgumentNullException("destinationFileName"); + throw new ArgumentNullException(nameof(destinationFileName)); Contract.EndContractBlock(); InternalReplace(sourceFileName, destinationFileName, destinationBackupFileName, false); @@ -1078,41 +696,24 @@ namespace System.IO { public static void Replace(String sourceFileName, String destinationFileName, String destinationBackupFileName, bool ignoreMetadataErrors) { if (sourceFileName == null) - throw new ArgumentNullException("sourceFileName"); + throw new ArgumentNullException(nameof(sourceFileName)); if (destinationFileName == null) - throw new ArgumentNullException("destinationFileName"); + throw new ArgumentNullException(nameof(destinationFileName)); Contract.EndContractBlock(); InternalReplace(sourceFileName, destinationFileName, destinationBackupFileName, ignoreMetadataErrors); } - [System.Security.SecuritySafeCritical] private static void InternalReplace(String sourceFileName, String destinationFileName, String destinationBackupFileName, bool ignoreMetadataErrors) { Contract.Requires(sourceFileName != null); Contract.Requires(destinationFileName != null); - // Write permission to all three files, read permission to source - // and dest. - String fullSrcPath = Path.GetFullPathInternal(sourceFileName); - String fullDestPath = Path.GetFullPathInternal(destinationFileName); + String fullSrcPath = Path.GetFullPath(sourceFileName); + String fullDestPath = Path.GetFullPath(destinationFileName); String fullBackupPath = null; if (destinationBackupFileName != null) - fullBackupPath = Path.GetFullPathInternal(destinationBackupFileName); - -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read | FileSecurityStateAccess.Write, sourceFileName, fullSrcPath); - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Read | FileSecurityStateAccess.Write, destinationFileName, fullDestPath); - FileSecurityState backupState = new FileSecurityState(FileSecurityStateAccess.Read | FileSecurityStateAccess.Write, destinationBackupFileName, fullBackupPath); - sourceState.EnsureState(); - destState.EnsureState(); - backupState.EnsureState(); -#else - FileIOPermission perm = new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, new String[] { fullSrcPath, fullDestPath}); - if (destinationBackupFileName != null) - perm.AddPathList(FileIOPermissionAccess.Write, fullBackupPath); - perm.Demand(); -#endif + fullBackupPath = Path.GetFullPath(destinationBackupFileName); int flags = Win32Native.REPLACEFILE_WRITE_THROUGH; if (ignoreMetadataErrors) @@ -1126,7 +727,6 @@ namespace System.IO { // Returns 0 on success, otherwise a Win32 error code. Note that // classes should use -1 as the uninitialized state for dataInitialized. - [System.Security.SecurityCritical] // auto-generated internal static int FillAttributeInfo(String path, ref Win32Native.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) { int dataInitialised = 0; @@ -1172,7 +772,7 @@ namespace System.IO { catch { // if we're already returning an error, don't throw another one. if (!error) { - Contract.Assert(false, "File::FillAttributeInfo - FindClose failed!"); + Debug.Assert(false, "File::FillAttributeInfo - FindClose failed!"); __Error.WinIOError(); } } @@ -1187,7 +787,6 @@ namespace System.IO { } else { - // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetErrorMode will let us disable this, but we should set the error @@ -1222,34 +821,5 @@ namespace System.IO { return dataInitialised; } - - [System.Security.SecurityCritical] // auto-generated - private static FileStream OpenFile(String path, FileAccess access, out SafeFileHandle handle) - { - FileStream fs = new FileStream(path, FileMode.Open, access, FileShare.ReadWrite, 1); - handle = fs.SafeFileHandle; - - if (handle.IsInvalid) { - // Return a meaningful error, using the RELATIVE path to - // the file to avoid returning extra information to the caller. - - // NT5 oddity - when trying to open "C:\" as a FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int hr = Marshal.GetLastWin32Error(); - String FullPath = Path.GetFullPathInternal(path); - if (hr==__Error.ERROR_PATH_NOT_FOUND && FullPath.Equals(Directory.GetDirectoryRoot(FullPath))) - hr = __Error.ERROR_ACCESS_DENIED; - - - __Error.WinIOError(hr, path); - } - return fs; - } - - - // Defined in WinError.h - private const int ERROR_INVALID_PARAMETER = 87; - private const int ERROR_ACCESS_DENIED = 0x5; } } diff --git a/src/mscorlib/src/System/IO/FileAttributes.cs b/src/mscorlib/src/System/IO/FileAttributes.cs index 19d5f227d7..51ef597f9d 100644 --- a/src/mscorlib/src/System/IO/FileAttributes.cs +++ b/src/mscorlib/src/System/IO/FileAttributes.cs @@ -11,13 +11,13 @@ ===========================================================*/ using System; -namespace System.IO { +namespace System.IO +{ // File attributes for use with the FileEnumerator class. // These constants correspond to the constants in WinNT.h. - // -[Serializable] + [Serializable] [Flags] -[System.Runtime.InteropServices.ComVisible(true)] + [System.Runtime.InteropServices.ComVisible(true)] public enum FileAttributes { // From WinNT.h (FILE_ATTRIBUTE_XXX) @@ -35,17 +35,5 @@ namespace System.IO { Offline = 0x1000, NotContentIndexed = 0x2000, Encrypted = 0x4000, - -#if !FEATURE_CORECLR -#if FEATURE_COMINTEROP - [System.Runtime.InteropServices.ComVisible(false)] -#endif // FEATURE_COMINTEROP - IntegrityStream = 0x8000, - -#if FEATURE_COMINTEROP - [System.Runtime.InteropServices.ComVisible(false)] -#endif // FEATURE_COMINTEROP - NoScrubData = 0x20000, -#endif } } diff --git a/src/mscorlib/src/System/IO/FileInfo.cs b/src/mscorlib/src/System/IO/FileInfo.cs index 3ab1a5122e..32622c63a1 100644 --- a/src/mscorlib/src/System/IO/FileInfo.cs +++ b/src/mscorlib/src/System/IO/FileInfo.cs @@ -2,33 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** -** -** Purpose: A collection of methods for manipulating Files. -** -** April 09,2000 (some design refactorization) -** -===========================================================*/ - -using System; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif -using System.Security.Permissions; -using PermissionSet = System.Security.PermissionSet; using Win32Native = Microsoft.Win32.Win32Native; using System.Runtime.InteropServices; using System.Text; using System.Runtime.Serialization; using System.Globalization; using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.Contracts; -namespace System.IO { +namespace System.IO +{ // Class for creating FileStream objects, and some basic file management // routines such as Delete, etc. [Serializable] @@ -37,85 +21,40 @@ namespace System.IO { { private String _name; -#if FEATURE_CORECLR // Migrating InheritanceDemands requires this default ctor, so we can annotate it. -#if FEATURE_CORESYSTEM - [System.Security.SecurityCritical] -#else - [System.Security.SecuritySafeCritical] -#endif //FEATURE_CORESYSTEM private FileInfo(){} - [System.Security.SecurityCritical] - public static FileInfo UnsafeCreateFileInfo(String fileName) - { - if (fileName == null) - throw new ArgumentNullException("fileName"); - Contract.EndContractBlock(); - - FileInfo fi = new FileInfo(); - fi.Init(fileName, false); - return fi; - } -#endif - - [System.Security.SecuritySafeCritical] public FileInfo(String fileName) { if (fileName == null) - throw new ArgumentNullException("fileName"); + throw new ArgumentNullException(nameof(fileName)); Contract.EndContractBlock(); - Init(fileName, true); + Init(fileName); } - [System.Security.SecurityCritical] - private void Init(String fileName, bool checkHost) + private void Init(String fileName) { OriginalPath = fileName; - // Must fully qualify the path for the security check - String fullPath = Path.GetFullPathInternal(fileName); -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, fileName, fullPath); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false); -#endif - _name = Path.GetFileName(fileName); - FullPath = fullPath; + FullPath = Path.GetFullPath(fileName); DisplayPath = GetDisplayPath(fileName); } private String GetDisplayPath(String originalPath) { -#if FEATURE_CORECLR return Path.GetFileName(originalPath); -#else - return originalPath; -#endif - } - [System.Security.SecurityCritical] // auto-generated private FileInfo(SerializationInfo info, StreamingContext context) : base(info, context) { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.Read, new String[] { FullPath }, false, false).Demand(); -#endif _name = Path.GetFileName(OriginalPath); DisplayPath = GetDisplayPath(OriginalPath); } -#if FEATURE_CORESYSTEM - [System.Security.SecuritySafeCritical] -#endif //FEATURE_CORESYSTEM internal FileInfo(String fullPath, bool ignoreThis) { - Contract.Assert(Path.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!"); + Debug.Assert(PathInternal.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!"); _name = Path.GetFileName(fullPath); OriginalPath = _name; FullPath = fullPath; @@ -125,10 +64,8 @@ namespace System.IO { public override String Name { get { return _name; } } - - + public long Length { - [System.Security.SecuritySafeCritical] // auto-generated get { if (_dataInitialised == -1) Refresh(); @@ -146,20 +83,9 @@ namespace System.IO { /* Returns the name of the directory that the file is in */ public String DirectoryName { - [System.Security.SecuritySafeCritical] get { - String directoryName = Path.GetDirectoryName(FullPath); - if (directoryName != null) - { -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, DisplayPath, FullPath); - state.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { directoryName }, false, false).Demand(); -#endif - } - return directoryName; + return Path.GetDirectoryName(FullPath); } } @@ -171,7 +97,7 @@ namespace System.IO { String dirName = DirectoryName; if (dirName == null) return null; - return new DirectoryInfo(dirName); + return new DirectoryInfo(dirName); } } @@ -187,27 +113,9 @@ namespace System.IO { } } -#if FEATURE_MACL - public FileSecurity GetAccessControl() - { - return File.GetAccessControl(FullPath, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); - } - - public FileSecurity GetAccessControl(AccessControlSections includeSections) - { - return File.GetAccessControl(FullPath, includeSections); - } - - public void SetAccessControl(FileSecurity fileSecurity) - { - File.SetAccessControl(FullPath, fileSecurity); - } -#endif - - [System.Security.SecuritySafeCritical] // auto-generated public StreamReader OpenText() { - return new StreamReader(FullPath, Encoding.UTF8, true, StreamReader.DefaultBufferSize, false); + return new StreamReader(FullPath, Encoding.UTF8, true, StreamReader.DefaultBufferSize); } public StreamWriter CreateText() @@ -220,45 +128,33 @@ namespace System.IO { return new StreamWriter(FullPath,true); } - // Copies an existing file to a new file. An exception is raised if the // destination file already exists. Use the // Copy(String, String, boolean) method to allow // overwriting an existing file. - // - // The caller must have certain FileIOPermissions. The caller must have - // Read permission to sourceFileName - // and Write permissions to destFileName. - // public FileInfo CopyTo(String destFileName) { if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(destFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - destFileName = File.InternalCopy(FullPath, destFileName, false, true); + destFileName = File.InternalCopy(FullPath, destFileName, false); return new FileInfo(destFileName, false); } - // Copies an existing file to a new file. If overwrite is // false, then an IOException is thrown if the destination file // already exists. If overwrite is true, the file is // overwritten. - // - // The caller must have certain FileIOPermissions. The caller must have - // Read permission to sourceFileName and Create - // and Write permissions to destFileName. - // public FileInfo CopyTo(String destFileName, bool overwrite) { if (destFileName == null) - throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName")); + throw new ArgumentNullException(nameof(destFileName), Environment.GetResourceString("ArgumentNull_FileName")); if (destFileName.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - destFileName = File.InternalCopy(FullPath, destFileName, overwrite, true); + destFileName = File.InternalCopy(FullPath, destFileName, overwrite); return new FileInfo(destFileName, false); } @@ -271,22 +167,9 @@ namespace System.IO { // an exception. // // On NT, Delete will fail for a file that is open for normal I/O - // or a file that is memory mapped. On Win95, the file will be - // deleted irregardless of whether the file is being used. - // - // Your application must have Delete permission to the target file. - // - [System.Security.SecuritySafeCritical] + // or a file that is memory mapped. public override void Delete() { -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, DisplayPath, FullPath); - state.EnsureState(); -#else - // For security check, path should be resolved to an absolute path. - new FileIOPermission(FileIOPermissionAccess.Write, new String[] { FullPath }, false, false).Demand(); -#endif - bool r = Win32Native.DeleteFile(FullPath); if (!r) { int hr = Marshal.GetLastWin32Error(); @@ -297,25 +180,10 @@ namespace System.IO { } } - [ComVisible(false)] - public void Decrypt() - { - File.Decrypt(FullPath); - } - - [ComVisible(false)] - public void Encrypt() - { - File.Encrypt(FullPath); - } - // Tests if the given file exists. The result is true if the file // given by the specified path exists; otherwise, the result is // false. - // - // Your application must have Read permission for the target directory. public override bool Exists { - [System.Security.SecuritySafeCritical] // auto-generated get { try { if (_dataInitialised == -1) @@ -335,9 +203,6 @@ namespace System.IO { } } - - - // User must explicitly specify opening a new file or appending to one. public FileStream Open(FileMode mode) { return Open(mode, FileAccess.ReadWrite, FileShare.None); @@ -351,54 +216,28 @@ namespace System.IO { return new FileStream(FullPath, mode, access, share); } - -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] // auto-generated -#endif public FileStream OpenRead() { return new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false); } - public FileStream OpenWrite() { return new FileStream(FullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); } - - - - - // Moves a given file to a new location and potentially a new file name. // This method does work across volumes. - // - // The caller must have certain FileIOPermissions. The caller must - // have Read and Write permission to - // sourceFileName and Write - // permissions to destFileName. - // - [System.Security.SecuritySafeCritical] public void MoveTo(String destFileName) { if (destFileName==null) - throw new ArgumentNullException("destFileName"); + throw new ArgumentNullException(nameof(destFileName)); if (destFileName.Length==0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName"); + throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), nameof(destFileName)); Contract.EndContractBlock(); - String fullDestFileName = Path.GetFullPathInternal(destFileName); -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, DisplayPath, FullPath); - FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destFileName, fullDestFileName); - sourceState.EnsureState(); - destState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, new String[] { FullPath }, false, false).Demand(); - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false); -#endif - + string fullDestFileName = Path.GetFullPath(destFileName); + if (!Win32Native.MoveFile(FullPath, fullDestFileName)) __Error.WinIOError(); FullPath = fullDestFileName; diff --git a/src/mscorlib/src/System/IO/FileLoadException.cs b/src/mscorlib/src/System/IO/FileLoadException.cs index fabe2613c3..2b56c00191 100644 --- a/src/mscorlib/src/System/IO/FileLoadException.cs +++ b/src/mscorlib/src/System/IO/FileLoadException.cs @@ -91,7 +91,6 @@ namespace System.IO { if (StackTrace != null) s += Environment.NewLine + StackTrace; -#if FEATURE_FUSION try { if(FusionLog!=null) @@ -107,7 +106,6 @@ namespace System.IO { { } -#endif // FEATURE_FUSION return s; } @@ -117,7 +115,6 @@ namespace System.IO { _fileName = info.GetString("FileLoad_FileName"); -#if FEATURE_FUSION try { _fusionLog = info.GetString("FileLoad_FusionLog"); @@ -126,7 +123,6 @@ namespace System.IO { { _fusionLog = null; } -#endif } private FileLoadException(String fileName, String fusionLog,int hResult) @@ -138,15 +134,10 @@ namespace System.IO { SetMessageField(); } -#if FEATURE_FUSION public String FusionLog { - [System.Security.SecuritySafeCritical] // auto-generated - [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlEvidence | SecurityPermissionFlag.ControlPolicy)] get { return _fusionLog; } } -#endif // FEATURE_FUSION - [System.Security.SecurityCritical] // auto-generated_required public override void GetObjectData(SerializationInfo info, StreamingContext context) { // Serialize data for our base classes. base will verify info != null. base.GetObjectData(info, context); @@ -154,7 +145,6 @@ namespace System.IO { // Serialize data for this class info.AddValue("FileLoad_FileName", _fileName, typeof(String)); -#if FEATURE_FUSION try { info.AddValue("FileLoad_FusionLog", FusionLog, typeof(String)); @@ -162,10 +152,8 @@ namespace System.IO { catch (SecurityException) { } -#endif } - [System.Security.SecuritySafeCritical] // auto-generated internal static String FormatFileLoadExceptionMessage(String fileName, int hResult) { @@ -178,12 +166,10 @@ namespace System.IO { return String.Format(CultureInfo.CurrentCulture, format, fileName, message); } - [System.Security.SecurityCritical] // auto-generated [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] private static extern void GetFileLoadExceptionMessage(int hResult, StringHandleOnStack retString); - [System.Security.SecurityCritical] // auto-generated [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] [SuppressUnmanagedCodeSecurity] private static extern void GetMessageForHR(int hresult, StringHandleOnStack retString); diff --git a/src/mscorlib/src/System/IO/FileNotFoundException.cs b/src/mscorlib/src/System/IO/FileNotFoundException.cs index 933e4fd94c..8cc75f8232 100644 --- a/src/mscorlib/src/System/IO/FileNotFoundException.cs +++ b/src/mscorlib/src/System/IO/FileNotFoundException.cs @@ -93,7 +93,6 @@ namespace System.IO { if (StackTrace != null) s += Environment.NewLine + StackTrace; -#if FEATURE_FUSION try { if(FusionLog!=null) @@ -109,7 +108,6 @@ namespace System.IO { { } -#endif return s; } @@ -118,7 +116,6 @@ namespace System.IO { // Base class constructor will check info != null. _fileName = info.GetString("FileNotFound_FileName"); -#if FEATURE_FUSION try { _fusionLog = info.GetString("FileNotFound_FusionLog"); @@ -127,7 +124,6 @@ namespace System.IO { { _fusionLog = null; } -#endif } private FileNotFoundException(String fileName, String fusionLog,int hResult) @@ -139,15 +135,10 @@ namespace System.IO { SetMessageField(); } -#if FEATURE_FUSION public String FusionLog { - [System.Security.SecuritySafeCritical] // auto-generated - [SecurityPermissionAttribute( SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlEvidence | SecurityPermissionFlag.ControlPolicy)] get { return _fusionLog; } } -#endif - [System.Security.SecurityCritical] // auto-generated_required public override void GetObjectData(SerializationInfo info, StreamingContext context) { // Serialize data for our base classes. base will verify info != null. base.GetObjectData(info, context); @@ -155,7 +146,6 @@ namespace System.IO { // Serialize data for this class info.AddValue("FileNotFound_FileName", _fileName, typeof(String)); -#if FEATURE_FUSION try { info.AddValue("FileNotFound_FusionLog", FusionLog, typeof(String)); @@ -163,7 +153,6 @@ namespace System.IO { catch (SecurityException) { } -#endif } } } diff --git a/src/mscorlib/src/System/IO/FileSecurityState.cs b/src/mscorlib/src/System/IO/FileSecurityState.cs deleted file mode 100644 index 249848ac02..0000000000 --- a/src/mscorlib/src/System/IO/FileSecurityState.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** Enum: FileSecurityState -** -** -** -** -** Purpose: Determines whether file system access is safe -** -** -===========================================================*/ - -using System; -using System.Diagnostics.Contracts; -using System.IO; -using System.Security; -using System.Security.Permissions; - -namespace System.IO -{ - [SecurityCritical] - [System.Runtime.CompilerServices.FriendAccessAllowed] - internal class FileSecurityState : SecurityState - { -#if !PLATFORM_UNIX - private static readonly char[] m_illegalCharacters = { '?', '*' }; -#endif // !PLATFORM_UNIX - - private FileSecurityStateAccess m_access; - private String m_userPath; - private String m_canonicalizedPath; - - // default ctor needed for security rule consistency - [SecurityCritical] - private FileSecurityState() - { - } - - internal FileSecurityState(FileSecurityStateAccess access, String path) - { - if (path == null) - { - throw new ArgumentNullException("path"); - } - VerifyAccess(access); - m_access = access; - m_userPath = path; - if (path.Equals(String.Empty, StringComparison.OrdinalIgnoreCase)) - { - m_canonicalizedPath = String.Empty; - } - else - { - VerifyPath(path); - m_canonicalizedPath = System.IO.Path.GetFullPathInternal(path); - } - } - - // slight perf savings for trusted internal callers - internal FileSecurityState(FileSecurityStateAccess access, String path, String canonicalizedPath) - { - VerifyAccess(access); - VerifyPath(path); - VerifyPath(canonicalizedPath); - - m_access = access; - m_userPath = path; - m_canonicalizedPath = canonicalizedPath; - } - - internal FileSecurityStateAccess Access - { - get - { - return m_access; - } - } - - public String Path { - [System.Runtime.CompilerServices.FriendAccessAllowed] - get - { - return m_canonicalizedPath; - } - } - - #if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated - #endif - public override void EnsureState() - { - // this is the case for empty string machine name, etc - if (String.Empty.Equals(m_canonicalizedPath)) - return; - - if (!IsStateAvailable()) - { - throw new SecurityException(Environment.GetResourceString("FileSecurityState_OperationNotPermitted", (m_userPath == null) ? String.Empty : m_userPath)); - } - } - - internal static FileSecurityStateAccess ToFileSecurityState(FileIOPermissionAccess access) - { - Contract.Requires((access & ~FileIOPermissionAccess.AllAccess) == 0); - return (FileSecurityStateAccess)access; // flags are identical; just cast - } - - private static void VerifyAccess(FileSecurityStateAccess access) - { - if ((access & ~FileSecurityStateAccess.AllAccess) != 0) - throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("Arg_EnumIllegalVal")); - } - - private static void VerifyPath(String path) - { - if (path != null) - { - path = path.Trim(); - -#if !PLATFORM_UNIX - if (!PathInternal.IsDevice(path) && PathInternal.HasInvalidVolumeSeparator(path)) - throw new ArgumentException(Environment.GetResourceString("Argument_PathFormatNotSupported")); -#endif - - System.IO.Path.CheckInvalidPathChars(path, checkAdditional: true); - } - } - } -} diff --git a/src/mscorlib/src/System/IO/FileSecurityStateAccess.cs b/src/mscorlib/src/System/IO/FileSecurityStateAccess.cs deleted file mode 100644 index b6378c6142..0000000000 --- a/src/mscorlib/src/System/IO/FileSecurityStateAccess.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** Enum: FileSecurityStateAccess -** -** -** -** -** Purpose: FileSecurityState enum -** -** -===========================================================*/ - -using System; - -namespace System.IO -{ - [Flags] - internal enum FileSecurityStateAccess - { - NoAccess = 0, - Read = 1, - Write = 2, - Append = 4, - PathDiscovery = 8, - AllAccess = 15 - } -} - diff --git a/src/mscorlib/src/System/IO/FileStream.cs b/src/mscorlib/src/System/IO/FileStream.cs deleted file mode 100644 index deef30c480..0000000000 --- a/src/mscorlib/src/System/IO/FileStream.cs +++ /dev/null @@ -1,2695 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** -** Purpose: Exposes a Stream around a file, with full -** synchronous and asychronous support, and buffering. -** -** -===========================================================*/ -using System; -using Microsoft.Win32; -using Microsoft.Win32.SafeHandles; -using System.Security; -#if FEATURE_MACL -using System.Security.AccessControl; -#endif -using System.Security.Permissions; -using System.Threading; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -#if FEATURE_REMOTING -using System.Runtime.Remoting.Messaging; -#endif -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; -using System.Diagnostics.Tracing; - -/* - * FileStream supports different modes of accessing the disk - async mode - * and sync mode. They are two completely different codepaths in the - * sync & async methods (ie, Read/Write vs. BeginRead/BeginWrite). File - * handles in NT can be opened in only sync or overlapped (async) mode, - * and we have to deal with this pain. Stream has implementations of - * the sync methods in terms of the async ones, so we'll - * call through to our base class to get those methods when necessary. - * - * Also buffering is added into FileStream as well. Folded in the - * code from BufferedStream, so all the comments about it being mostly - * aggressive (and the possible perf improvement) apply to FileStream as - * well. Also added some buffering to the async code paths. - * - * Class Invariants: - * The class has one buffer, shared for reading & writing. It can only be - * used for one or the other at any point in time - not both. The following - * should be true: - * 0 <= _readPos <= _readLen < _bufferSize - * 0 <= _writePos < _bufferSize - * _readPos == _readLen && _readPos > 0 implies the read buffer is valid, - * but we're at the end of the buffer. - * _readPos == _readLen == 0 means the read buffer contains garbage. - * Either _writePos can be greater than 0, or _readLen & _readPos can be - * greater than zero, but neither can be greater than zero at the same time. - * - */ - -namespace System.IO { - - // This is an internal object implementing IAsyncResult with fields - // for all of the relevant data necessary to complete the IO operation. - // This is used by AsyncFSCallback and all of the async methods. - // We should probably make this a nested type of FileStream. But - // I don't know how to define a nested class in mscorlib.h - - unsafe internal sealed class FileStreamAsyncResult : IAsyncResult - { - // README: - // If you modify the order of these fields, make sure to update - // the native VM definition of this class as well!!! - // User code callback - private AsyncCallback _userCallback; - private Object _userStateObject; - private ManualResetEvent _waitHandle; - [System.Security.SecurityCritical] - private SafeFileHandle _handle; // For cancellation support. - - [SecurityCritical] - private NativeOverlapped* _overlapped; - internal NativeOverlapped* OverLapped { [SecurityCritical]get { return _overlapped; } } - internal bool IsAsync { [SecuritySafeCritical]get { return _overlapped != null; } } - - - internal int _EndXxxCalled; // Whether we've called EndXxx already. - private int _numBytes; // number of bytes read OR written - internal int NumBytes { get { return _numBytes; } } - - private int _errorCode; - internal int ErrorCode { get { return _errorCode; } } - - private int _numBufferedBytes; - internal int NumBufferedBytes { get { return _numBufferedBytes; } } - - internal int NumBytesRead { get { return _numBytes + _numBufferedBytes; } } - - private bool _isWrite; // Whether this is a read or a write - internal bool IsWrite { get { return _isWrite; } } - - private bool _isComplete; // Value for IsCompleted property - private bool _completedSynchronously; // Which thread called callback - - // The NativeOverlapped struct keeps a GCHandle to this IAsyncResult object. - // So if the user doesn't call EndRead/EndWrite, a finalizer won't help because - // it'll never get called. - - // Overlapped class will take care of the async IO operations in progress - // when an appdomain unload occurs. - - [System.Security.SecurityCritical] // auto-generated - private unsafe static IOCompletionCallback s_IOCallback; - - [SecuritySafeCritical] - internal FileStreamAsyncResult( - int numBufferedBytes, - byte[] bytes, - SafeFileHandle handle, - AsyncCallback userCallback, - Object userStateObject, - bool isWrite) - { - _userCallback = userCallback; - _userStateObject = userStateObject; - _isWrite = isWrite; - _numBufferedBytes = numBufferedBytes; - _handle = handle; - - // For Synchronous IO, I could go with either a callback and using - // the managed Monitor class, or I could create a handle and wait on it. - ManualResetEvent waitHandle = new ManualResetEvent(false); - _waitHandle = waitHandle; - - // Create a managed overlapped class - // We will set the file offsets later - Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, this); - - // Pack the Overlapped class, and store it in the async result - if (userCallback != null) - { - var ioCallback = s_IOCallback; // cached static delegate; delay initialized due to it being SecurityCritical - if (ioCallback == null) s_IOCallback = ioCallback = new IOCompletionCallback(AsyncFSCallback); - _overlapped = overlapped.Pack(ioCallback, bytes); - } - else - { - _overlapped = overlapped.UnsafePack(null, bytes); - } - - Contract.Assert(_overlapped != null, "Did Overlapped.Pack or Overlapped.UnsafePack just return a null?"); - } - - internal static FileStreamAsyncResult CreateBufferedReadResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject, bool isWrite) - { - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(numBufferedBytes, userCallback, userStateObject, isWrite); - asyncResult.CallUserCallback(); - return asyncResult; - } - - // This creates a synchronous Async Result. We should consider making this a separate class and maybe merge it with - // System.IO.Stream.SynchronousAsyncResult - private FileStreamAsyncResult(int numBufferedBytes, AsyncCallback userCallback, Object userStateObject, bool isWrite) - { - _userCallback = userCallback; - _userStateObject = userStateObject; - _isWrite = isWrite; - _numBufferedBytes = numBufferedBytes; - } - - public Object AsyncState - { - get { return _userStateObject; } - } - - public bool IsCompleted - { - get { return _isComplete; } - } - - public WaitHandle AsyncWaitHandle - { - [System.Security.SecuritySafeCritical] // auto-generated - get { - // Consider uncommenting this someday soon - the EventHandle - // in the Overlapped struct is really useless half of the - // time today since the OS doesn't signal it. If users call - // EndXxx after the OS call happened to complete, there's no - // reason to create a synchronization primitive here. Fixing - // this will save us some perf, assuming we can correctly - // initialize the ManualResetEvent. - if (_waitHandle == null) { - ManualResetEvent mre = new ManualResetEvent(false); - if (_overlapped != null && _overlapped->EventHandle != IntPtr.Zero) { - mre.SafeWaitHandle = new SafeWaitHandle(_overlapped->EventHandle, true); - } - - // make sure only one thread sets _waitHandle - if (Interlocked.CompareExchange<ManualResetEvent>(ref _waitHandle, mre, null) == null) { - if (_isComplete) - _waitHandle.Set(); - } - else { - // There's a slight but acceptable race condition if we weren't - // the thread that set _waitHandle and this code path - // returns before the code in the if statement - // executes (on the other thread). However, the - // caller is waiting for the wait handle to be set, - // which will still happen. - mre.Close(); - } - } - return _waitHandle; - } - } - - // Returns true iff the user callback was called by the thread that - // called BeginRead or BeginWrite. If we use an async delegate or - // threadpool thread internally, this will be false. This is used - // by code to determine whether a successive call to BeginRead needs - // to be done on their main thread or in their callback to avoid a - // stack overflow on many reads or writes. - public bool CompletedSynchronously - { - get { return _completedSynchronously; } - } - - private void CallUserCallbackWorker() - { - _isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - if (_waitHandle != null) - _waitHandle.Set(); - - _userCallback(this); - } - - internal void CallUserCallback() - { - // Convenience method for me, since I have to do this in a number - // of places in the buffering code for fake IAsyncResults. - // AsyncFSCallback intentionally does not use this method. - - if (_userCallback != null) { - // Call user's callback on a threadpool thread. - // Set completedSynchronously to false, since it's on another - // thread, not the main thread. - _completedSynchronously = false; - ThreadPool.QueueUserWorkItem(state => ((FileStreamAsyncResult)state).CallUserCallbackWorker(), this); - } - else { - _isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - if (_waitHandle != null) - _waitHandle.Set(); - } - } - - [SecurityCritical] - internal void ReleaseNativeResource() - { - // Free memory & GC handles. - if (this._overlapped != null) - Overlapped.Free(_overlapped); - } - - internal void Wait() - { - if (_waitHandle != null) - { - // We must block to ensure that AsyncFSCallback has completed, - // and we should close the WaitHandle in here. AsyncFSCallback - // and the hand-ported imitation version in COMThreadPool.cpp - // are the only places that set this event. - try - { - _waitHandle.WaitOne(); - Contract.Assert(_isComplete == true, "FileStreamAsyncResult::Wait - AsyncFSCallback didn't set _isComplete to true!"); - } - finally - { - _waitHandle.Close(); - } - } - } - - // When doing IO asynchronously (ie, _isAsync==true), this callback is - // called by a free thread in the threadpool when the IO operation - // completes. - [System.Security.SecurityCritical] // auto-generated - unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) - { - BCLDebug.Log(String.Format("AsyncFSCallback called. errorCode: " + errorCode + " numBytes: " + numBytes)); - - // Unpack overlapped - Overlapped overlapped = Overlapped.Unpack(pOverlapped); - // Free the overlapped struct in EndRead/EndWrite. - - // Extract async result from overlapped - FileStreamAsyncResult asyncResult = - (FileStreamAsyncResult)overlapped.AsyncResult; - asyncResult._numBytes = (int)numBytes; - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferReceive((long)(asyncResult.OverLapped), 2, string.Empty); - - // Handle reading from & writing to closed pipes. While I'm not sure - // this is entirely necessary anymore, maybe it's possible for - // an async read on a pipe to be issued and then the pipe is closed, - // returning this error. This may very well be necessary. - if (errorCode == FileStream.ERROR_BROKEN_PIPE || errorCode == FileStream.ERROR_NO_DATA) - errorCode = 0; - - asyncResult._errorCode = (int)errorCode; - - // Call the user-provided callback. It can and often should - // call EndRead or EndWrite. There's no reason to use an async - // delegate here - we're already on a threadpool thread. - // IAsyncResult's completedSynchronously property must return - // false here, saying the user callback was called on another thread. - asyncResult._completedSynchronously = false; - asyncResult._isComplete = true; - - // ensure _isComplete is set before reading _waitHandle - Thread.MemoryBarrier(); - - // The OS does not signal this event. We must do it ourselves. - ManualResetEvent wh = asyncResult._waitHandle; - if (wh != null) - { - Contract.Assert(!wh.SafeWaitHandle.IsClosed, "ManualResetEvent already closed!"); - bool r = wh.Set(); - Contract.Assert(r, "ManualResetEvent::Set failed!"); - if (!r) __Error.WinIOError(); - } - - AsyncCallback userCallback = asyncResult._userCallback; - if (userCallback != null) - userCallback(asyncResult); - } - - [SecuritySafeCritical] - [HostProtection(ExternalThreading = true)] - internal void Cancel() - { - Contract.Assert(_handle != null, "_handle should not be null."); - Contract.Assert(_overlapped != null, "Cancel should only be called on true asynchronous FileStreamAsyncResult, i.e. _overlapped is not null"); - - if (IsCompleted) - return; - - if (_handle.IsInvalid) - return; - - bool r = Win32Native.CancelIoEx(_handle, _overlapped); - if (!r) - { - int errorCode = Marshal.GetLastWin32Error(); - - // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. - // This probably means that the IO operation has completed. - if (errorCode != Win32Native.ERROR_NOT_FOUND) - __Error.WinIOError(errorCode, String.Empty); - } - } - } - - [ComVisible(true)] - public class FileStream : Stream - { - internal const int DefaultBufferSize = 4096; - - private byte[] _buffer; // Shared read/write buffer. Alloc on first use. - private String _fileName; // Fully qualified file name. - private bool _isAsync; // Whether we opened the handle for overlapped IO - private bool _canRead; - private bool _canWrite; - private bool _canSeek; - private bool _exposedHandle; // Could other code be using this handle? - private bool _isPipe; // Whether to disable async buffering code. - private int _readPos; // Read pointer within shared buffer. - private int _readLen; // Number of bytes read in buffer from file. - private int _writePos; // Write pointer within shared buffer. - private int _bufferSize; // Length of internal buffer, if it's allocated. - [System.Security.SecurityCritical] // auto-generated - private SafeFileHandle _handle; - private long _pos; // Cache current location in the file. - private long _appendStart;// When appending, prevent overwriting file. - private static AsyncCallback s_endReadTask; - private static AsyncCallback s_endWriteTask; - private static Action<object> s_cancelReadHandler; - private static Action<object> s_cancelWriteHandler; - - //This exists only to support IsolatedStorageFileStream. - //Any changes to FileStream must include the corresponding changes in IsolatedStorage. - internal FileStream() { - } -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode) - : this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access) - : this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access, FileShare share) - : this(path, mode, access, share, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) { - } - - [System.Security.SecuritySafeCritical] - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize) - : this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false, false, true) - { - } - -#else // FEATURE_CORECLR - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode) - : this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access) - : this(path, mode, access, FileShare.Read, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share) - : this(path, mode, access, share, DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize) - : this(path, mode, access, share, bufferSize, FileOptions.None, Path.GetFileName(path), false) - { - } -#endif // FEATURE_CORECLR - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - : this(path, mode, access, share, bufferSize, options, Path.GetFileName(path), false) - { - } - - #if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated - #else - [System.Security.SecuritySafeCritical] - #endif - public FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) - : this(path, mode, access, share, bufferSize, (useAsync ? FileOptions.Asynchronous : FileOptions.None), Path.GetFileName(path), false) - { - } - -#if FEATURE_MACL - // This constructor is done differently to avoid loading a few more - // classes, and more importantly, to build correctly on Rotor. - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity) - { - Object pinningHandle; - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share, fileSecurity, out pinningHandle); - try { - Init(path, mode, (FileAccess)0, (int)rights, true, share, bufferSize, options, secAttrs, Path.GetFileName(path), false, false, false); - } - finally { - if (pinningHandle != null) { - GCHandle pinHandle = (GCHandle) pinningHandle; - pinHandle.Free(); - } - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(String path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, (FileAccess)0, (int)rights, true, share, bufferSize, options, secAttrs, Path.GetFileName(path), false, false, false); - } -#endif - - [System.Security.SecurityCritical] // auto-generated - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, false, false); - } - - [System.Security.SecurityCritical] - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy, bool useLongPath) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, useLongPath, false); - } - - [System.Security.SecurityCritical] - internal FileStream(String path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, String msgPath, bool bFromProxy, bool useLongPath, bool checkHost) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - Init(path, mode, access, 0, false, share, bufferSize, options, secAttrs, msgPath, bFromProxy, useLongPath, checkHost); - } - - // AccessControl namespace is not defined in Rotor - [System.Security.SecuritySafeCritical] - private void Init(String path, FileMode mode, FileAccess access, int rights, bool useRights, FileShare share, int bufferSize, FileOptions options, Win32Native.SECURITY_ATTRIBUTES secAttrs, String msgPath, bool bFromProxy, bool useLongPath, bool checkHost) - { - if (path == null) - throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - Contract.EndContractBlock(); - -#if FEATURE_MACL - FileSystemRights fileSystemRights = (FileSystemRights)rights; -#endif - // msgPath must be safe to hand back to untrusted code. - - _fileName = msgPath; // To handle odd cases of finalizing partially constructed objects. - _exposedHandle = false; - - // don't include inheritable in our bounds check for share - FileShare tempshare = share & ~FileShare.Inheritable; - String badArg = null; - - if (mode < FileMode.CreateNew || mode > FileMode.Append) - badArg = "mode"; - else if (!useRights && (access < FileAccess.Read || access > FileAccess.ReadWrite)) - badArg = "access"; -#if FEATURE_MACL - else if (useRights && (fileSystemRights < FileSystemRights.ReadData || fileSystemRights > FileSystemRights.FullControl)) - badArg = "rights"; -#endif - else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) - badArg = "share"; - - if (badArg != null) - throw new ArgumentOutOfRangeException(badArg, Environment.GetResourceString("ArgumentOutOfRange_Enum")); - - // NOTE: any change to FileOptions enum needs to be matched here in the error validation - if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) - throw new ArgumentOutOfRangeException("options", Environment.GetResourceString("ArgumentOutOfRange_Enum")); - - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); - - // Write access validation -#if FEATURE_MACL - if ((!useRights && (access & FileAccess.Write) == 0) - || (useRights && (fileSystemRights & FileSystemRights.Write) == 0)) -#else - if (!useRights && (access & FileAccess.Write) == 0) -#endif //FEATURE_MACL - { - if (mode==FileMode.Truncate || mode==FileMode.CreateNew || mode==FileMode.Create || mode==FileMode.Append) { - // No write access - if (!useRights) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&AccessCombo", mode, access)); -#if FEATURE_MACL - else - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileMode&RightsCombo", mode, fileSystemRights)); -#endif //FEATURE_MACL - } - } - -#if FEATURE_MACL - // FileMode.Truncate only works with GENERIC_WRITE (FileAccess.Write), source:MSDN - // For backcomp use FileAccess.Write when FileSystemRights.Write is specified - if (useRights && (mode == FileMode.Truncate)) { - if (fileSystemRights == FileSystemRights.Write) { - useRights = false; - access = FileAccess.Write; - } - else { - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFileModeTruncate&RightsCombo", mode, fileSystemRights)); - } - } -#endif - - int fAccess; - if (!useRights) { - fAccess = access == FileAccess.Read? GENERIC_READ: - access == FileAccess.Write? GENERIC_WRITE: - GENERIC_READ | GENERIC_WRITE; - } - else { - fAccess = rights; - } - - // Get absolute path - Security needs this to prevent something - // like trying to create a file in c:\tmp with the name - // "..\WinNT\System32\ntoskrnl.exe". Store it for user convenience. - int maxPath = useLongPath ? Path.MaxLongPath : Path.MaxPath; - String filePath = Path.NormalizePath(path, true, maxPath); - - _fileName = filePath; - - // Prevent access to your disk drives as raw block devices. - if (filePath.StartsWith("\\\\.\\", StringComparison.Ordinal)) - throw new ArgumentException(Environment.GetResourceString("Arg_DevicesNotSupported")); - - // In 4.0, we always construct a FileIOPermission object below. - // If filePath contained a ':', we would throw a NotSupportedException in - // System.Security.Util.StringExpressionSet.CanonicalizePath. - // If filePath contained other illegal characters, we would throw an ArgumentException in - // FileIOPermission.CheckIllegalCharacters. - // In 4.5 we on longer construct the FileIOPermission object in full trust. - // To preserve the 4.0 behavior we do an explicit check for ':' here and also call Path.CheckInvalidPathChars. - // Note that we need to call CheckInvalidPathChars before checking for ':' because that is what FileIOPermission does. - - Path.CheckInvalidPathChars(filePath, true); - -#if !PLATFORM_UNIX - if (filePath.IndexOf( ':', 2 ) != -1) - throw new NotSupportedException( Environment.GetResourceString( "Argument_PathFormatNotSupported" ) ); -#endif // !PLATFORM_UNIX - - bool read = false; - -#if FEATURE_MACL - if ((!useRights && (access & FileAccess.Read) != 0) || (useRights && (fileSystemRights & FileSystemRights.ReadAndExecute) != 0)) -#else - if (!useRights && (access & FileAccess.Read) != 0) -#endif //FEATURE_MACL - { - if (mode == FileMode.Append) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidAppendMode")); - else - read = true; - } - - // All demands in full trust domains are no-ops, so skip -#if FEATURE_CAS_POLICY - if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) -#endif // FEATURE_CAS_POLICY - { - // Build up security permissions required, as well as validate we - // have a sensible set of parameters. IE, creating a brand new file - // for reading doesn't make much sense. - FileIOPermissionAccess secAccess = FileIOPermissionAccess.NoAccess; - - if (read) - { - Contract.Assert(mode != FileMode.Append); - secAccess = secAccess | FileIOPermissionAccess.Read; - } - - // I can't think of any combos of FileMode we should disallow if we - // don't have read access. Writing would pretty much always be valid - // in those cases. - - // For any FileSystemRights other than ReadAndExecute, demand Write permission - // This is probably bit overkill for TakeOwnership etc but we don't have any - // matching FileIOPermissionAccess to demand. It is better that we ask for Write permission. - -#if FEATURE_MACL - // FileMode.OpenOrCreate & FileSystemRights.Synchronize can create 0-byte file; demand write - if ((!useRights && (access & FileAccess.Write) != 0) - || (useRights && (fileSystemRights & (FileSystemRights.Write | FileSystemRights.Delete - | FileSystemRights.DeleteSubdirectoriesAndFiles - | FileSystemRights.ChangePermissions - | FileSystemRights.TakeOwnership)) != 0) - || (useRights && ((fileSystemRights & FileSystemRights.Synchronize) != 0) - && mode==FileMode.OpenOrCreate) - ) -#else - if (!useRights && (access & FileAccess.Write) != 0) -#endif //FEATURE_MACL - { - if (mode==FileMode.Append) - secAccess = secAccess | FileIOPermissionAccess.Append; - else - secAccess = secAccess | FileIOPermissionAccess.Write; - } - -#if FEATURE_MACL - bool specifiedAcl; - unsafe { - specifiedAcl = secAttrs != null && secAttrs.pSecurityDescriptor != null; - } - - AccessControlActions control = specifiedAcl ? AccessControlActions.Change : AccessControlActions.None; - new FileIOPermission(secAccess, control, new String[] { filePath }, false, false).Demand(); -#else -#if FEATURE_CORECLR - if (checkHost) { - FileSecurityState state = new FileSecurityState(FileSecurityState.ToFileSecurityState(secAccess), path, filePath); - state.EnsureState(); - } -#else - new FileIOPermission(secAccess, new String[] { filePath }, false, false).Demand(); -#endif // FEATURE_CORECLR -#endif - } - - // Our Inheritable bit was stolen from Windows, but should be set in - // the security attributes class. Don't leave this bit set. - share &= ~FileShare.Inheritable; - - bool seekToEnd = (mode==FileMode.Append); - // Must use a valid Win32 constant here... - if (mode == FileMode.Append) - mode = FileMode.OpenOrCreate; - - // WRT async IO, do the right thing for whatever platform we're on. - // This way, someone can easily write code that opens a file - // asynchronously no matter what their platform is. - if ((options & FileOptions.Asynchronous) != 0) - _isAsync = true; - else - options &= ~FileOptions.Asynchronous; - - int flagsAndAttributes = (int) options; - -#if !PLATFORM_UNIX - // For mitigating local elevation of privilege attack through named pipes - // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the - // named pipe server can't impersonate a high privileged client security context - flagsAndAttributes |= (Win32Native.SECURITY_SQOS_PRESENT | Win32Native.SECURITY_ANONYMOUS); -#endif - - // Don't pop up a dialog for reading from an emtpy floppy drive - int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); - try { - String tempPath = filePath; - if (useLongPath) - tempPath = Path.AddLongPathPrefix(tempPath); - _handle = Win32Native.SafeCreateFile(tempPath, fAccess, share, secAttrs, mode, flagsAndAttributes, IntPtr.Zero); - - if (_handle.IsInvalid) { - // Return a meaningful exception, using the RELATIVE path to - // the file to avoid returning extra information to the caller - // unless they have path discovery permission, in which case - // the full path is fine & useful. - - // NT5 oddity - when trying to open "C:\" as a FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode==__Error.ERROR_PATH_NOT_FOUND && filePath.Equals(Directory.InternalGetDirectoryRoot(filePath))) - errorCode = __Error.ERROR_ACCESS_DENIED; - - // We need to give an exception, and preferably it would include - // the fully qualified path name. Do security check here. If - // we fail, give back the msgPath, which should not reveal much. - // While this logic is largely duplicated in - // __Error.WinIOError, we need this for - // IsolatedStorageFileStream. - bool canGiveFullPath = false; - - if (!bFromProxy) - { - try { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false ).Demand(); -#endif - canGiveFullPath = true; - } - catch(SecurityException) {} - } - - if (canGiveFullPath) - __Error.WinIOError(errorCode, _fileName); - else - __Error.WinIOError(errorCode, msgPath); - } - } - finally { - Win32Native.SetErrorMode(oldMode); - } - - // Disallow access to all non-file devices from the FileStream - // constructors that take a String. Everyone else can call - // CreateFile themselves then use the constructor that takes an - // IntPtr. Disallows "con:", "com1:", "lpt1:", etc. - int fileType = Win32Native.GetFileType(_handle); - if (fileType != Win32Native.FILE_TYPE_DISK) { - _handle.Close(); - throw new NotSupportedException(Environment.GetResourceString("NotSupported_FileStreamOnNonFiles")); - } - - // This is necessary for async IO using IO Completion ports via our - // managed Threadpool API's. This (theoretically) calls the OS's - // BindIoCompletionCallback method, and passes in a stub for the - // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped - // struct for this request and gets a delegate to a managed callback - // from there, which it then calls on a threadpool thread. (We allocate - // our native OVERLAPPED structs 2 pointers too large and store EE state - // & GC handles there, one to an IAsyncResult, the other to a delegate.) - if (_isAsync) { - bool b = false; - // BindHandle requires UnmanagedCode permission -#pragma warning disable 618 - new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); -#pragma warning restore 618 - try { - b = ThreadPool.BindHandle(_handle); - } - finally { - CodeAccessPermission.RevertAssert(); - if (!b) { - // We should close the handle so that the handle is not open until SafeFileHandle GC - Contract.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?"); - _handle.Close(); - } - } - if (!b) - throw new IOException(Environment.GetResourceString("IO.IO_BindHandleFailed")); - } - - if (!useRights) { - _canRead = (access & FileAccess.Read) != 0; - _canWrite = (access & FileAccess.Write) != 0; - } -#if FEATURE_MACL - else { - _canRead = (fileSystemRights & FileSystemRights.ReadData) != 0; - _canWrite = ((fileSystemRights & FileSystemRights.WriteData) != 0) - || ((fileSystemRights & FileSystemRights.AppendData) != 0); - } -#endif //FEATURE_MACL - - _canSeek = true; - _isPipe = false; - _pos = 0; - _bufferSize = bufferSize; - _readPos = 0; - _readLen = 0; - _writePos = 0; - - // For Append mode... - if (seekToEnd) { - _appendStart = SeekCore(0, SeekOrigin.End); - } - else { - _appendStart = -1; - } - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access) - : this(handle, access, true, DefaultBufferSize, false) { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle) - : this(handle, access, ownsHandle, DefaultBufferSize, false) { - } - - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) - : this(handle, access, ownsHandle, bufferSize, false) { - } - - // We explicitly do a Demand, not a LinkDemand here. - [System.Security.SecuritySafeCritical] // auto-generated - [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")] -#pragma warning disable 618 - [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#pragma warning restore 618 - public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) - : this(new SafeFileHandle(handle, ownsHandle), access, bufferSize, isAsync) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(SafeFileHandle handle, FileAccess access) - : this(handle, access, DefaultBufferSize, false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) - : this(handle, access, bufferSize, false) { - } - - [System.Security.SecuritySafeCritical] // auto-generated -#pragma warning disable 618 - [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#pragma warning restore 618 - public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { - // To ensure we don't leak a handle, put it in a SafeFileHandle first - if (handle.IsInvalid) - throw new ArgumentException(Environment.GetResourceString("Arg_InvalidHandle"), "handle"); - Contract.EndContractBlock(); - - _handle = handle; - _exposedHandle = true; - - // Now validate arguments. - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum")); - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); - - int handleType = Win32Native.GetFileType(_handle); - Contract.Assert(handleType == Win32Native.FILE_TYPE_DISK || handleType == Win32Native.FILE_TYPE_PIPE || handleType == Win32Native.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!"); - _isAsync = isAsync; - _canRead = 0 != (access & FileAccess.Read); - _canWrite = 0 != (access & FileAccess.Write); - _canSeek = handleType == Win32Native.FILE_TYPE_DISK; - _bufferSize = bufferSize; - _readPos = 0; - _readLen = 0; - _writePos = 0; - _fileName = null; - _isPipe = handleType == Win32Native.FILE_TYPE_PIPE; - - // This is necessary for async IO using IO Completion ports via our - // managed Threadpool API's. This calls the OS's - // BindIoCompletionCallback method, and passes in a stub for the - // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped - // struct for this request and gets a delegate to a managed callback - // from there, which it then calls on a threadpool thread. (We allocate - // our native OVERLAPPED structs 2 pointers too large and store EE - // state & a handle to a delegate there.) -#if !FEATURE_CORECLR - if (_isAsync) { - bool b = false; - try { - b = ThreadPool.BindHandle(_handle); - } - catch (ApplicationException) { - // If you passed in a synchronous handle and told us to use - // it asynchronously, throw here. - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotAsync")); - } - if (!b) { - throw new IOException(Environment.GetResourceString("IO.IO_BindHandleFailed")); - } - } - else { -#endif // FEATURE_CORECLR - if (handleType != Win32Native.FILE_TYPE_PIPE) - VerifyHandleIsSync(); -#if !FEATURE_CORECLR - } -#endif // FEATURE_CORECLR - - if (_canSeek) - SeekCore(0, SeekOrigin.Current); - else - _pos = 0; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private static Win32Native.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) - { - Win32Native.SECURITY_ATTRIBUTES secAttrs = null; - if ((share & FileShare.Inheritable) != 0) { - secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); - - secAttrs.bInheritHandle = 1; - } - return secAttrs; - } - -#if FEATURE_MACL - // If pinningHandle is not null, caller must free it AFTER the call to - // CreateFile has returned. - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe static Win32Native.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share, FileSecurity fileSecurity, out Object pinningHandle) - { - pinningHandle = null; - Win32Native.SECURITY_ATTRIBUTES secAttrs = null; - if ((share & FileShare.Inheritable) != 0 || fileSecurity != null) { - secAttrs = new Win32Native.SECURITY_ATTRIBUTES(); - secAttrs.nLength = (int)Marshal.SizeOf(secAttrs); - - if ((share & FileShare.Inheritable) != 0) { - secAttrs.bInheritHandle = 1; - } - - // For ACL's, get the security descriptor from the FileSecurity. - if (fileSecurity != null) { - byte[] sd = fileSecurity.GetSecurityDescriptorBinaryForm(); - pinningHandle = GCHandle.Alloc(sd, GCHandleType.Pinned); - fixed(byte* pSecDescriptor = sd) - secAttrs.pSecurityDescriptor = pSecDescriptor; - } - } - return secAttrs; - } -#endif - - // Verifies that this handle supports synchronous IO operations (unless you - // didn't open it for either reading or writing). - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe void VerifyHandleIsSync() - { - // Do NOT use this method on pipes. Reading or writing to a pipe may - // cause an app to block incorrectly, introducing a deadlock (depending - // on whether a write will wake up an already-blocked thread or this - // FileStream's thread). - - // Do NOT change this to use a byte[] of length 0, or test test won't - // work. Our ReadFile & WriteFile methods are special cased to return - // for arrays of length 0, since we'd get an IndexOutOfRangeException - // while using C#'s fixed syntax. - byte[] bytes = new byte[1]; - int hr = 0; - int r = 0; - - // If the handle is a pipe, ReadFile will block until there - // has been a write on the other end. We'll just have to deal with it, - // For the read end of a pipe, you can mess up and - // accidentally read synchronously from an async pipe. - if (CanRead) { - r = ReadFileNative(_handle, bytes, 0, 0, null, out hr); - } - else if (CanWrite) { - r = WriteFileNative(_handle, bytes, 0, 0, null, out hr); - } - - if (hr==ERROR_INVALID_PARAMETER) - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync")); - if (hr == Win32Native.ERROR_INVALID_HANDLE) - __Error.WinIOError(hr, "<OS handle>"); - } - - - public override bool CanRead { - [Pure] - get { return _canRead; } - } - - public override bool CanWrite { - [Pure] - get { return _canWrite; } - } - - public override bool CanSeek { - [Pure] - get { return _canSeek; } - } - - public virtual bool IsAsync { - get { return _isAsync; } - } - - public override long Length { - [System.Security.SecuritySafeCritical] // auto-generated - get { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - int hi = 0, lo = 0; - - lo = Win32Native.GetFileSize(_handle, out hi); - - if (lo==-1) { // Check for either an error or a 4GB - 1 byte file. - int hr = Marshal.GetLastWin32Error(); - if (hr != 0) - __Error.WinIOError(hr, String.Empty); - } - long len = (((long)hi) << 32) | ((uint) lo); - // If we're writing near the end of the file, we must include our - // internal buffer in our Length calculation. Don't flush because - // we use the length of the file in our async write method. - if (_writePos > 0 && _pos + _writePos > len) - len = _writePos + _pos; - return len; - } - } - - public String Name { - [System.Security.SecuritySafeCritical] - get { - if (_fileName == null) - return Environment.GetResourceString("IO_UnknownFileName"); -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, _fileName); - sourceState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { _fileName }, false, false).Demand(); -#endif - return _fileName; - } - } - - internal String NameInternal { - get { - if (_fileName == null) - return "<UnknownFileName>"; - return _fileName; - } - } - - public override long Position { - [System.Security.SecuritySafeCritical] // auto-generated - get { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - // Verify that internal position is in sync with the handle - if (_exposedHandle) - VerifyOSHandlePosition(); - - // Compensate for buffer that we read from the handle (_readLen) Vs what the user - // read so far from the internel buffer (_readPos). Of course add any unwrittern - // buffered data - return _pos + (_readPos - _readLen + _writePos); - } - set { - if (value < 0) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_writePos > 0) FlushWrite(false); - _readPos = 0; - _readLen = 0; - Seek(value, SeekOrigin.Begin); - } - } - -#if FEATURE_MACL - [System.Security.SecuritySafeCritical] // auto-generated - public FileSecurity GetAccessControl() - { - if (_handle.IsClosed) __Error.FileNotOpen(); - return new FileSecurity(_handle, _fileName, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public void SetAccessControl(FileSecurity fileSecurity) - { - if (fileSecurity == null) - throw new ArgumentNullException("fileSecurity"); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - fileSecurity.Persist(_handle, _fileName); - } -#endif - - [System.Security.SecuritySafeCritical] // auto-generated - protected override void Dispose(bool disposing) - { - // Nothing will be done differently based on whether we are - // disposing vs. finalizing. This is taking advantage of the - // weak ordering between normal finalizable objects & critical - // finalizable objects, which I included in the SafeHandle - // design for FileStream, which would often "just work" when - // finalized. - try { - if (_handle != null && !_handle.IsClosed) { - // Flush data to disk iff we were writing. After - // thinking about this, we also don't need to flush - // our read position, regardless of whether the handle - // was exposed to the user. They probably would NOT - // want us to do this. - if (_writePos > 0) { - FlushWrite(!disposing); - } - } - } - finally { - if (_handle != null && !_handle.IsClosed) - _handle.Dispose(); - - _canRead = false; - _canWrite = false; - _canSeek = false; - // Don't set the buffer to null, to avoid a NullReferenceException - // when users have a race condition in their code (ie, they call - // Close when calling another method on Stream like Read). - //_buffer = null; - base.Dispose(disposing); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - ~FileStream() - { - if (_handle != null) { - BCLDebug.Correctness(_handle.IsClosed, "You didn't close a FileStream & it got finalized. Name: \""+_fileName+"\""); - Dispose(false); - } - } - - public override void Flush() - { - Flush(false); - } - - [System.Security.SecuritySafeCritical] - public virtual void Flush(Boolean flushToDisk) - { - // This code is duplicated in Dispose - if (_handle.IsClosed) __Error.FileNotOpen(); - - FlushInternalBuffer(); - - if (flushToDisk && CanWrite) - { - FlushOSBuffer(); - } - } - - private void FlushInternalBuffer() - { - if (_writePos > 0) - { - FlushWrite(false); - } - else if (_readPos < _readLen && CanSeek) - { - FlushRead(); - } - } - - [System.Security.SecuritySafeCritical] - private void FlushOSBuffer() - { - if (!Win32Native.FlushFileBuffers(_handle)) - { - __Error.WinIOError(); - } - } - - // Reading is done by blocks from the file, but someone could read - // 1 byte from the buffer then write. At that point, the OS's file - // pointer is out of sync with the stream's position. All write - // functions should call this function to preserve the position in the file. - private void FlushRead() { - Contract.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushRead!"); - if (_readPos - _readLen != 0) { - Contract.Assert(CanSeek, "FileStream will lose buffered read data now."); - SeekCore(_readPos - _readLen, SeekOrigin.Current); - } - _readPos = 0; - _readLen = 0; - } - - // Writes are buffered. Anytime the buffer fills up - // (_writePos + delta > _bufferSize) or the buffer switches to reading - // and there is left over data (_writePos > 0), this function must be called. - private void FlushWrite(bool calledFromFinalizer) { - Contract.Assert(_readPos == 0 && _readLen == 0, "FileStream: Read buffer must be empty in FlushWrite!"); - - if (_isAsync) { - IAsyncResult asyncResult = BeginWriteCore(_buffer, 0, _writePos, null, null); - // With our Whidbey async IO & overlapped support for AD unloads, - // we don't strictly need to block here to release resources - // since that support takes care of the pinning & freeing the - // overlapped struct. We need to do this when called from - // Close so that the handle is closed when Close returns, but - // we do't need to call EndWrite from the finalizer. - // Additionally, if we do call EndWrite, we block forever - // because AD unloads prevent us from running the managed - // callback from the IO completion port. Blocking here when - // called from the finalizer during AD unload is clearly wrong, - // but we can't use any sort of test for whether the AD is - // unloading because if we weren't unloading, an AD unload - // could happen on a separate thread before we call EndWrite. - if (!calledFromFinalizer) - EndWrite(asyncResult); - } - else - WriteCore(_buffer, 0, _writePos); - - _writePos = 0; - } - - - [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")] - public virtual IntPtr Handle { - [System.Security.SecurityCritical] // auto-generated_required -#if !FEATURE_CORECLR - [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#endif - get { - Flush(); - // Explicitly dump any buffered data, since the user could move our - // position or write to the file. - _readPos = 0; - _readLen = 0; - _writePos = 0; - _exposedHandle = true; - - return _handle.DangerousGetHandle(); - } - } - - public virtual SafeFileHandle SafeFileHandle { - [System.Security.SecurityCritical] // auto-generated_required -#if !FEATURE_CORECLR - [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] -#endif - get { - Flush(); - // Explicitly dump any buffered data, since the user could move our - // position or write to the file. - _readPos = 0; - _readLen = 0; - _writePos = 0; - _exposedHandle = true; - - return _handle; - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void SetLength(long value) - { - if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - if (!CanWrite) __Error.WriteNotSupported(); - - // Handle buffering updates. - if (_writePos > 0) { - FlushWrite(false); - } - else if (_readPos < _readLen) { - FlushRead(); - } - _readPos = 0; - _readLen = 0; - - if (_appendStart != -1 && value < _appendStart) - throw new IOException(Environment.GetResourceString("IO.IO_SetLengthAppendTruncate")); - SetLengthCore(value); - } - - // We absolutely need this method broken out so that BeginWriteCore can call - // a method without having to go through buffering code that might call - // FlushWrite. - [System.Security.SecuritySafeCritical] // auto-generated - private void SetLengthCore(long value) - { - Contract.Assert(value >= 0, "value >= 0"); - long origPos = _pos; - - if (_exposedHandle) - VerifyOSHandlePosition(); - if (_pos != value) - SeekCore(value, SeekOrigin.Begin); - if (!Win32Native.SetEndOfFile(_handle)) { - int hr = Marshal.GetLastWin32Error(); - if (hr==__Error.ERROR_INVALID_PARAMETER) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_FileLengthTooBig")); - __Error.WinIOError(hr, String.Empty); - } - // Return file pointer to where it was before setting length - if (origPos != value) { - if (origPos < value) - SeekCore(origPos, SeekOrigin.Begin); - else - SeekCore(0, SeekOrigin.End); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override int Read([In, Out] byte[] array, int offset, int count) { - if (array==null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - bool isBlocked = false; - int n = _readLen - _readPos; - // if the read buffer is empty, read into either user's array or our - // buffer, depending on number of bytes user asked for and buffer size. - if (n == 0) { - if (!CanRead) __Error.ReadNotSupported(); - if (_writePos > 0) FlushWrite(false); - if (!CanSeek || (count >= _bufferSize)) { - n = ReadCore(array, offset, count); - // Throw away read buffer. - _readPos = 0; - _readLen = 0; - return n; - } - if (_buffer == null) _buffer = new byte[_bufferSize]; - n = ReadCore(_buffer, 0, _bufferSize); - if (n == 0) return 0; - isBlocked = n < _bufferSize; - _readPos = 0; - _readLen = n; - } - // Now copy min of count or numBytesAvailable (ie, near EOF) to array. - if (n > count) n = count; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - // We may have read less than the number of bytes the user asked - // for, but that is part of the Stream contract. Reading again for - // more data may cause us to block if we're using a device with - // no clear end of file, such as a serial port or pipe. If we - // blocked here & this code was used with redirected pipes for a - // process's standard output, this can lead to deadlocks involving - // two processes. But leave this here for files to avoid what would - // probably be a breaking change. -- - - // If we are reading from a device with no clear EOF like a - // serial port or a pipe, this will cause us to block incorrectly. - if (!_isPipe) { - // If we hit the end of the buffer and didn't have enough bytes, we must - // read some more from the underlying stream. However, if we got - // fewer bytes from the underlying stream than we asked for (ie, we're - // probably blocked), don't ask for more bytes. - if (n < count && !isBlocked) { - Contract.Assert(_readPos == _readLen, "Read buffer should be empty!"); - int moreBytesRead = ReadCore(array, offset + n, count - n); - n += moreBytesRead; - // We've just made our buffer inconsistent with our position - // pointer. We must throw away the read buffer. - _readPos = 0; - _readLen = 0; - } - } - - return n; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe int ReadCore(byte[] buffer, int offset, int count) { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanRead, "CanRead"); - - Contract.Assert(buffer != null, "buffer != null"); - Contract.Assert(_writePos == 0, "_writePos == 0"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(count >= 0, "count is negative"); - - if (_isAsync) { - IAsyncResult result = BeginReadCore(buffer, offset, count, null, null, 0); - return EndRead(result); - } - - // Make sure we are reading from the right spot - if (_exposedHandle) - VerifyOSHandlePosition(); - - int hr = 0; - int r = ReadFileNative(_handle, buffer, offset, count, null, out hr); - if (r == -1) { - // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. - if (hr == ERROR_BROKEN_PIPE) { - r = 0; - } - else { - if (hr == ERROR_INVALID_PARAMETER) - throw new ArgumentException(Environment.GetResourceString("Arg_HandleNotSync")); - - __Error.WinIOError(hr, String.Empty); - } - } - Contract.Assert(r >= 0, "FileStream's ReadCore is likely broken."); - _pos += r; - - return r; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override long Seek(long offset, SeekOrigin origin) { - if (origin<SeekOrigin.Begin || origin>SeekOrigin.End) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - if (!CanSeek) __Error.SeekNotSupported(); - - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - // If we've got bytes in our buffer to write, write them out. - // If we've read in and consumed some bytes, we'll have to adjust - // our seek positions ONLY IF we're seeking relative to the current - // position in the stream. This simulates doing a seek to the new - // position, then a read for the number of bytes we have in our buffer. - if (_writePos > 0) { - FlushWrite(false); - } - else if (origin == SeekOrigin.Current) { - // Don't call FlushRead here, which would have caused an infinite - // loop. Simply adjust the seek origin. This isn't necessary - // if we're seeking relative to the beginning or end of the stream. - offset -= (_readLen - _readPos); - } - - // Verify that internal position is in sync with the handle - if (_exposedHandle) - VerifyOSHandlePosition(); - - long oldPos = _pos + (_readPos - _readLen); - long pos = SeekCore(offset, origin); - - // Prevent users from overwriting data in a file that was opened in - // append mode. - if (_appendStart != -1 && pos < _appendStart) { - SeekCore(oldPos, SeekOrigin.Begin); - throw new IOException(Environment.GetResourceString("IO.IO_SeekAppendOverwrite")); - } - - // We now must update the read buffer. We can in some cases simply - // update _readPos within the buffer, copy around the buffer so our - // Position property is still correct, and avoid having to do more - // reads from the disk. Otherwise, discard the buffer's contents. - if (_readLen > 0) { - // We can optimize the following condition: - // oldPos - _readPos <= pos < oldPos + _readLen - _readPos - if (oldPos == pos) { - if (_readPos > 0) { - //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen); - Buffer.InternalBlockCopy(_buffer, _readPos, _buffer, 0, _readLen - _readPos); - _readLen -= _readPos; - _readPos = 0; - } - // If we still have buffered data, we must update the stream's - // position so our Position property is correct. - if (_readLen > 0) - SeekCore(_readLen, SeekOrigin.Current); - } - else if (oldPos - _readPos < pos && pos < oldPos + _readLen - _readPos) { - int diff = (int)(pos - oldPos); - //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff)); - Buffer.InternalBlockCopy(_buffer, _readPos+diff, _buffer, 0, _readLen - (_readPos + diff)); - _readLen -= (_readPos + diff); - _readPos = 0; - if (_readLen > 0) - SeekCore(_readLen, SeekOrigin.Current); - } - else { - // Lose the read buffer. - _readPos = 0; - _readLen = 0; - } - Contract.Assert(_readLen >= 0 && _readPos <= _readLen, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen"); - Contract.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled."); - } - return pos; - } - - // 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 - [System.Security.SecuritySafeCritical] // auto-generated - private long SeekCore(long offset, SeekOrigin origin) { - Contract.Assert(!_handle.IsClosed && CanSeek, "!_handle.IsClosed && CanSeek"); - Contract.Assert(origin>=SeekOrigin.Begin && origin<=SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End"); - int hr = 0; - long ret = 0; - - ret = Win32Native.SetFilePointer(_handle, offset, origin, out hr); - if (ret == -1) { - // #errorInvalidHandle - // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set - // the handle as invalid; the handle must also be closed. - // - // Marking the handle as invalid but not closing the handle - // resulted in exceptions during finalization and locked column - // values (due to invalid but unclosed handle) in SQL FileStream - // scenarios. - // - // A more mainstream scenario involves accessing a file on a - // network share. ERROR_INVALID_HANDLE may occur because the network - // connection was dropped and the server closed the handle. However, - // the client side handle is still open and even valid for certain - // operations. - // - // Note that Dispose doesn't throw so we don't need to special case. - // SetHandleAsInvalid only sets _closed field to true (without - // actually closing handle) so we don't need to call that as well. - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - __Error.WinIOError(hr, String.Empty); - } - - _pos = ret; - return ret; - } - - // Checks the position of the OS's handle equals what we expect it to. - // This will fail if someone else moved the FileStream's handle or if - // we've hit a bug in FileStream's position updating code. - private void VerifyOSHandlePosition() - { - if (!CanSeek) - return; - - // SeekCore will override the current _pos, so save it now - long oldPos = _pos; - long curPos = SeekCore(0, SeekOrigin.Current); - - if (curPos != oldPos) { - // For reads, this is non-fatal but we still could have returned corrupted - // data in some cases. So discard the internal buffer. Potential MDA - _readPos = 0; - _readLen = 0; - if(_writePos > 0) { - // Discard the buffer and let the user know! - _writePos = 0; - throw new IOException(Environment.GetResourceString("IO.IO_FileStreamHandlePosition")); - } - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void Write(byte[] array, int offset, int count) { - if (array==null) - throw new ArgumentNullException("array", Environment.GetResourceString("ArgumentNull_Buffer")); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (_writePos == 0) - { - // Ensure we can write to the stream, and ready buffer for writing. - if (!CanWrite) __Error.WriteNotSupported(); - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - } - - // If our buffer has data in it, copy data from the user's array into - // the buffer, and if we can fit it all there, return. Otherwise, write - // the buffer to disk and copy any remaining data into our buffer. - // The assumption here is memcpy is cheaper than disk (or net) IO. - // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) - // So the extra copying will reduce the total number of writes, in - // non-pathological cases (ie, write 1 byte, then write for the buffer - // size repeatedly) - if (_writePos > 0) { - int numBytes = _bufferSize - _writePos; // space left in buffer - if (numBytes > 0) { - if (numBytes > count) - numBytes = count; - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, numBytes); - _writePos += numBytes; - if (count==numBytes) return; - offset += numBytes; - count -= numBytes; - } - // Reset our buffer. We essentially want to call FlushWrite - // without calling Flush on the underlying Stream. - - if (_isAsync) { - IAsyncResult result = BeginWriteCore(_buffer, 0, _writePos, null, null); - EndWrite(result); - } - else - { - WriteCore(_buffer, 0, _writePos); - } - - _writePos = 0; - } - // If the buffer would slow writes down, avoid buffer completely. - if (count >= _bufferSize) { - Contract.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); - WriteCore(array, offset, count); - return; - } - else if (count == 0) - return; // Don't allocate a buffer then call memcpy for 0 bytes. - if (_buffer==null) _buffer = new byte[_bufferSize]; - // Copy remaining bytes into buffer, to write at a later date. - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, count); - _writePos = count; - return; - } - - [System.Security.SecuritySafeCritical] // auto-generated - private unsafe void WriteCore(byte[] buffer, int offset, int count) { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanWrite, "CanWrite"); - - Contract.Assert(buffer != null, "buffer != null"); - Contract.Assert(_readPos == _readLen, "_readPos == _readLen"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(count >= 0, "count is negative"); - - if (_isAsync) { - IAsyncResult result = BeginWriteCore(buffer, offset, count, null, null); - EndWrite(result); - return; - } - - // Make sure we are writing to the position that we think we are - if (_exposedHandle) - VerifyOSHandlePosition(); - - int hr = 0; - int r = WriteFileNative(_handle, buffer, offset, count, null, out hr); - if (r == -1) { - // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. - if (hr == ERROR_NO_DATA) { - r = 0; - } - else { - // ERROR_INVALID_PARAMETER may be returned for writes - // where the position is too large (ie, writing at Int64.MaxValue - // on Win9x) OR for synchronous writes to a handle opened - // asynchronously. - if (hr == ERROR_INVALID_PARAMETER) - throw new IOException(Environment.GetResourceString("IO.IO_FileTooLongOrHandleNotSync")); - __Error.WinIOError(hr, String.Empty); - } - } - Contract.Assert(r >= 0, "FileStream's WriteCore is likely broken."); - _pos += r; - return; - } - - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - if (array==null) - throw new ArgumentNullException("array"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (numBytes < 0) - throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < numBytes) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (!_isAsync) - return base.BeginRead(array, offset, numBytes, userCallback, stateObject); - else - return BeginReadAsync(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - private FileStreamAsyncResult BeginReadAsync(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(_isAsync); - - if (!CanRead) __Error.ReadNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - if (_isPipe) - { - // When redirecting stdout & stderr with the Process class, it's easy to deadlock your - // parent & child processes when doing writes 4K at a time. The - // OS appears to use a 4K buffer internally. If you write to a - // pipe that is full, you will block until someone read from - // that pipe. If you try reading from an empty pipe and - // FileStream's BeginRead blocks waiting for data to fill it's - // internal buffer, you will be blocked. In a case where a child - // process writes to stdout & stderr while a parent process tries - // reading from both, you can easily get into a deadlock here. - // To avoid this deadlock, don't buffer when doing async IO on - // pipes. But don't completely ignore buffered data either. - if (_readPos < _readLen) - { - int n = _readLen - _readPos; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - Contract.Assert(_writePos == 0, "FileStream must not have buffered write data here! Pipes should be unidirectional."); - return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0); - } - } - - Contract.Assert(!_isPipe, "Should not be a pipe."); - - // Handle buffering. - if (_writePos > 0) FlushWrite(false); - if (_readPos == _readLen) - { - // I can't see how to handle buffering of async requests when - // filling the buffer asynchronously, without a lot of complexity. - // The problems I see are issuing an async read, we do an async - // read to fill the buffer, then someone issues another read - // (either synchronously or asynchronously) before the first one - // returns. This would involve some sort of complex buffer locking - // that we probably don't want to get into, at least not in V1. - // If we did a sync read to fill the buffer, we could avoid the - // problem, and any async read less than 64K gets turned into a - // synchronous read by NT anyways... -- - - if (numBytes < _bufferSize) - { - if (_buffer == null) _buffer = new byte[_bufferSize]; - IAsyncResult bufferRead = BeginReadCore(_buffer, 0, _bufferSize, null, null, 0); - _readLen = EndRead(bufferRead); - int n = _readLen; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, 0, array, offset, n); - _readPos = n; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - // Here we're making our position pointer inconsistent - // with our read buffer. Throw away the read buffer's contents. - _readPos = 0; - _readLen = 0; - return BeginReadCore(array, offset, numBytes, userCallback, stateObject, 0); - } - } - else - { - int n = _readLen - _readPos; - if (n > numBytes) n = numBytes; - Buffer.InternalBlockCopy(_buffer, _readPos, array, offset, n); - _readPos += n; - - if (n >= numBytes) - { - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(n, userCallback, stateObject, false); - } - else - { - // For streams with no clear EOF like serial ports or pipes - // we cannot read more data without causing an app to block - // incorrectly. Pipes don't go down this path - // though. This code needs to be fixed. - // Throw away read buffer. - _readPos = 0; - _readLen = 0; - return BeginReadCore(array, offset + n, numBytes - n, userCallback, stateObject, n); - } - // WARNING: all state on asyncResult objects must be set before - // we call ReadFile in BeginReadCore, since the OS can run our - // callback & the user's callback before ReadFile returns. - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - unsafe private FileStreamAsyncResult BeginReadCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject, int numBufferedBytesRead) - { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanRead, "CanRead"); - Contract.Assert(bytes != null, "bytes != null"); - Contract.Assert(_writePos == 0, "_writePos == 0"); - Contract.Assert(_isAsync, "BeginReadCore doesn't work on synchronous file streams!"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(numBytes >= 0, "numBytes is negative"); - - // Create and store async stream class library specific data in the async result - - // Must pass in _numBufferedBytes here to ensure all the state on the IAsyncResult - // object is set before we call ReadFile, which gives the OS an - // opportunity to run our callback (including the user callback & - // the call to EndRead) before ReadFile has returned. - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(numBufferedBytesRead, bytes, _handle, userCallback, stateObject, false); - NativeOverlapped* intOverlapped = asyncResult.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 - if (_exposedHandle) - VerifyOSHandlePosition(); - - if (_pos + numBytes > len) { - if (_pos <= len) - numBytes = (int) (len - _pos); - else - numBytes = 0; - } - - // 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)_pos); - intOverlapped->OffsetHigh = (int)(_pos>>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(numBytes, SeekOrigin.Current); - } - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferSend((long)(asyncResult.OverLapped), 2, string.Empty, false); - - // queue an async ReadFile operation and pass in a packed overlapped - int hr = 0; - int r = ReadFileNative(_handle, bytes, offset, numBytes, intOverlapped, out hr); - // 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/ hr==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 && numBytes!=-1) { - - // For pipes, when they hit EOF, they will come here. - if (hr == 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; - asyncResult.CallUserCallback(); - // EndRead will free the Overlapped struct correctly. - } - else if (hr != ERROR_IO_PENDING) { - if (!_handle.IsClosed && CanSeek) // Update Position - It could be anywhere. - SeekCore(0, SeekOrigin.Current); - - if (hr == ERROR_HANDLE_EOF) - __Error.EndOfFile(); - else - __Error.WinIOError(hr, String.Empty); - } - } - 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. - //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread"); - } - - return asyncResult; - } - - [System.Security.SecuritySafeCritical] // Although the unsafe code is only required in PAL, the block is wide scoped. Leave it here for desktop to ensure it's reviewed. - public unsafe override int EndRead(IAsyncResult asyncResult) - { - // There are 3 significantly different IAsyncResults we'll accept - // here. One is from Stream::BeginRead. The other two are variations - // on our FileStreamAsyncResult. One is from BeginReadCore, - // while the other is from the BeginRead buffering wrapper. - if (asyncResult==null) - throw new ArgumentNullException("asyncResult"); - Contract.EndContractBlock(); - - if (!_isAsync) - return base.EndRead(asyncResult); - - FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult; - if (afsar==null || afsar.IsWrite) - __Error.WrongAsyncResult(); - - // Ensure we don't have any race conditions by doing an interlocked - // CompareExchange here. Avoids corrupting memory via freeing the - // NativeOverlapped class or GCHandle twice. -- - if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) - __Error.EndReadCalledTwice(); - - // Obtain the WaitHandle, but don't use public property in case we - // delay initialize the manual reset event in the future. - afsar.Wait(); - - // Free memory & GC handles. - afsar.ReleaseNativeResource(); - - // Now check for any error during the read. - if (afsar.ErrorCode != 0) - __Error.WinIOError(afsar.ErrorCode, String.Empty); - - return afsar.NumBytesRead; - } - - // Reads a byte from the file stream. Returns the byte cast to an int - // or -1 if reading from the end of the stream. - [System.Security.SecuritySafeCritical] // auto-generated - public override int ReadByte() { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (_readLen==0 && !CanRead) __Error.ReadNotSupported(); - Contract.Assert((_readPos==0 && _readLen==0 && _writePos >= 0) || (_writePos==0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - if (_readPos == _readLen) { - if (_writePos > 0) FlushWrite(false); - Contract.Assert(_bufferSize > 0, "_bufferSize > 0"); - if (_buffer == null) _buffer = new byte[_bufferSize]; - _readLen = ReadCore(_buffer, 0, _bufferSize); - _readPos = 0; - } - if (_readPos == _readLen) - return -1; - - int result = _buffer[_readPos]; - _readPos++; - return result; - } - - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading=true)] - public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - if (array==null) - throw new ArgumentNullException("array"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (numBytes < 0) - throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (array.Length - offset < numBytes) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (_handle.IsClosed) __Error.FileNotOpen(); - - if (!_isAsync) - return base.BeginWrite(array, offset, numBytes, userCallback, stateObject); - else - return BeginWriteAsync(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - [HostProtection(ExternalThreading = true)] - private FileStreamAsyncResult BeginWriteAsync(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(_isAsync); - - if (!CanWrite) __Error.WriteNotSupported(); - - Contract.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), "We're either reading or writing, but not both."); - - if (_isPipe) - { - // When redirecting stdout & stderr with the Process class, it's easy to deadlock your - // parent & child processes when doing writes 4K at a time. The - // OS appears to use a 4K buffer internally. If you write to a - // pipe that is full, you will block until someone read from - // that pipe. If you try reading from an empty pipe and - // FileStream's BeginRead blocks waiting for data to fill it's - // internal buffer, you will be blocked. In a case where a child - // process writes to stdout & stderr while a parent process tries - // reading from both, you can easily get into a deadlock here. - // To avoid this deadlock, don't buffer when doing async IO on - // pipes. - Contract.Assert(_readPos == 0 && _readLen == 0, "FileStream must not have buffered data here! Pipes should be unidirectional."); - - if (_writePos > 0) - FlushWrite(false); - - return BeginWriteCore(array, offset, numBytes, userCallback, stateObject); - } - - // Handle buffering. - if (_writePos == 0) - { - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - } - - int n = _bufferSize - _writePos; - if (numBytes <= n) - { - if (_writePos == 0) _buffer = new byte[_bufferSize]; - Buffer.InternalBlockCopy(array, offset, _buffer, _writePos, numBytes); - _writePos += numBytes; - - // Return a synchronous FileStreamAsyncResult - return FileStreamAsyncResult.CreateBufferedReadResult(numBytes, userCallback, stateObject, true); - } - - if (_writePos > 0) - FlushWrite(false); - - return BeginWriteCore(array, offset, numBytes, userCallback, stateObject); - } - - [System.Security.SecuritySafeCritical] // auto-generated - unsafe private FileStreamAsyncResult BeginWriteCore(byte[] bytes, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) - { - Contract.Assert(!_handle.IsClosed, "!_handle.IsClosed"); - Contract.Assert(CanWrite, "CanWrite"); - Contract.Assert(bytes != null, "bytes != null"); - Contract.Assert(_readPos == _readLen, "_readPos == _readLen"); - Contract.Assert(_isAsync, "BeginWriteCore doesn't work on synchronous file streams!"); - Contract.Assert(offset >= 0, "offset is negative"); - Contract.Assert(numBytes >= 0, "numBytes is negative"); - - // Create and store async stream class library specific data in the async result - FileStreamAsyncResult asyncResult = new FileStreamAsyncResult(0, bytes, _handle, userCallback, stateObject, true); - NativeOverlapped* intOverlapped = asyncResult.OverLapped; - - if (CanSeek) { - // Make sure we set the length of the file appropriately. - long len = Length; - //Console.WriteLine("BeginWrite - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes); - - // Make sure we are writing to the position that we think we are - if (_exposedHandle) - VerifyOSHandlePosition(); - - if (_pos + numBytes > len) { - //Console.WriteLine("BeginWrite - Setting length to: "+(pos + numBytes)); - SetLengthCore(_pos + numBytes); - } - - // 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)_pos; - intOverlapped->OffsetHigh = (int)(_pos>>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(numBytes, SeekOrigin.Current); - } - - //Console.WriteLine("BeginWrite finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position); - - if (FrameworkEventSource.IsInitialized && FrameworkEventSource.Log.IsEnabled(EventLevel.Informational, FrameworkEventSource.Keywords.ThreadTransfer)) - FrameworkEventSource.Log.ThreadTransferSend((long)(asyncResult.OverLapped), 2, string.Empty, false); - - int hr = 0; - // queue an async WriteFile operation and pass in a packed overlapped - int r = WriteFileNative(_handle, bytes, offset, numBytes, intOverlapped, out hr); - - // 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/ hr==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 && numBytes!=-1) { - //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if hr==3e5) hr: 0x{0:x}", hr); - - // For pipes, when they are closed on the other side, they will come here. - if (hr == ERROR_NO_DATA) { - // Not an error, but EOF. AsyncFSCallback will NOT be - // called. Call the user callback here. - asyncResult.CallUserCallback(); - // EndWrite will free the Overlapped struct correctly. - } - else if (hr != ERROR_IO_PENDING) { - if (!_handle.IsClosed && CanSeek) // Update Position - It could be anywhere. - SeekCore(0, SeekOrigin.Current); - - if (hr == ERROR_HANDLE_EOF) - __Error.EndOfFile(); - else - __Error.WinIOError(hr, String.Empty); - } - } - 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. - //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread."); - } - - return asyncResult; - } - - [System.Security.SecuritySafeCritical] // Although the unsafe code is only required in PAL, the block is wide scoped. Leave it here for desktop to ensure it's reviewed. - public unsafe override void EndWrite(IAsyncResult asyncResult) - { - if (asyncResult==null) - throw new ArgumentNullException("asyncResult"); - Contract.EndContractBlock(); - - if (!_isAsync) { - base.EndWrite(asyncResult); - return; - } - - FileStreamAsyncResult afsar = asyncResult as FileStreamAsyncResult; - if (afsar==null || !afsar.IsWrite) - __Error.WrongAsyncResult(); - - // Ensure we can't have any race conditions by doing an interlocked - // CompareExchange here. Avoids corrupting memory via freeing the - // NativeOverlapped class or GCHandle twice. -- - if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) - __Error.EndWriteCalledTwice(); - - // Obtain the WaitHandle, but don't use public property in case we - // delay initialize the manual reset event in the future. - afsar.Wait(); - - // Free memory & GC handles. - afsar.ReleaseNativeResource(); - - // Now check for any error during the write. - if (afsar.ErrorCode != 0) - __Error.WinIOError(afsar.ErrorCode, String.Empty); - - // Number of bytes written is afsar._numBytes + afsar._numBufferedBytes. - return; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void WriteByte(byte value) - { - if (_handle.IsClosed) __Error.FileNotOpen(); - if (_writePos==0) { - if (!CanWrite) __Error.WriteNotSupported(); - if (_readPos < _readLen) FlushRead(); - _readPos = 0; - _readLen = 0; - Contract.Assert(_bufferSize > 0, "_bufferSize > 0"); - if (_buffer==null) _buffer = new byte[_bufferSize]; - } - if (_writePos == _bufferSize) - FlushWrite(false); - - _buffer[_writePos] = value; - _writePos++; - } - - [System.Security.SecuritySafeCritical] // auto-generated - public virtual void Lock(long position, long length) { - if (position < 0 || length < 0) - throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - - int positionLow = unchecked((int)(position )); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length )); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Win32Native.LockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh)) - __Error.WinIOError(); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public virtual void Unlock(long position, long length) { - if (position < 0 || length < 0) - throw new ArgumentOutOfRangeException((position < 0 ? "position" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - Contract.EndContractBlock(); - if (_handle.IsClosed) __Error.FileNotOpen(); - - int positionLow = unchecked((int)(position )); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length )); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Win32Native.UnlockFile(_handle, positionLow, positionHigh, lengthLow, lengthHigh)) - __Error.WinIOError(); - } - - // Windows API definitions, from winbase.h and others - - private const int FILE_ATTRIBUTE_NORMAL = 0x00000080; - private const int FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; - private const int FILE_FLAG_OVERLAPPED = 0x40000000; - internal const int GENERIC_READ = unchecked((int)0x80000000); - private const int GENERIC_WRITE = 0x40000000; - - private const int FILE_BEGIN = 0; - private const int FILE_CURRENT = 1; - private const int FILE_END = 2; - - // Error codes (not HRESULTS), from winerror.h - internal const int ERROR_BROKEN_PIPE = 109; - internal const int ERROR_NO_DATA = 232; - private const int ERROR_HANDLE_EOF = 38; - private const int ERROR_INVALID_PARAMETER = 87; - private const int ERROR_IO_PENDING = 997; - - - // __ConsoleStream also uses this code. - [System.Security.SecurityCritical] // auto-generated - private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) - { - Contract.Requires(handle != null, "handle != null"); - Contract.Requires(offset >= 0, "offset >= 0"); - Contract.Requires(count >= 0, "count >= 0"); - Contract.Requires(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition")); - Contract.EndContractBlock(); - - Contract.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter mismatch in call to ReadFileNative."); - - // You can't use the fixed statement on an array of length 0. - if (bytes.Length==0) { - hr = 0; - return 0; - } - - int r = 0; - int numBytesRead = 0; - - fixed(byte* p = bytes) { - if (_isAsync) - r = Win32Native.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Win32Native.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero); - } - - if (r==0) { - hr = Marshal.GetLastWin32Error(); - // We should never silently drop an error here without some - // extra work. We must make sure that BeginReadCore won't return an - // IAsyncResult that will cause EndRead to block, since the OS won't - // call AsyncFSCallback for us. - if (hr == ERROR_BROKEN_PIPE || hr == Win32Native.ERROR_PIPE_NOT_CONNECTED) { - // This handle was a pipe, and it's done. Not an error, but EOF. - // However, the OS will not call AsyncFSCallback! - // Let the caller handle this, since BeginReadCore & ReadCore - // need to do different things. - return -1; - } - - // See code:#errorInvalidHandle in "private long SeekCore(long offset, SeekOrigin origin)". - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - - return -1; - } - else - hr = 0; - return numBytesRead; - } - - [System.Security.SecurityCritical] // auto-generated - private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) { - Contract.Requires(handle != null, "handle != null"); - Contract.Requires(offset >= 0, "offset >= 0"); - Contract.Requires(count >= 0, "count >= 0"); - Contract.Requires(bytes != null, "bytes != null"); - // Don't corrupt memory when multiple threads are erroneously writing - // to this stream simultaneously. (the OS is reading from - // the array we pass to WriteFile, but if we read beyond the end and - // that memory isn't allocated, we could get an AV.) - if (bytes.Length - offset < count) - throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition")); - Contract.EndContractBlock(); - - Contract.Assert((_isAsync && overlapped != null) || (!_isAsync && overlapped == null), "Async IO parameter missmatch in call to WriteFileNative."); - - // You can't use the fixed statement on an array of length 0. - if (bytes.Length==0) { - hr = 0; - return 0; - } - - int numBytesWritten = 0; - int r = 0; - - fixed(byte* p = bytes) { - if (_isAsync) - r = Win32Native.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped); - else - r = Win32Native.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero); - } - - if (r==0) { - hr = Marshal.GetLastWin32Error(); - // We should never silently drop an error here without some - // extra work. We must make sure that BeginWriteCore won't return an - // IAsyncResult that will cause EndWrite to block, since the OS won't - // call AsyncFSCallback for us. - - if (hr==ERROR_NO_DATA) { - // This handle was a pipe, and the pipe is being closed on the - // other side. Let the caller handle this, since BeginWriteCore - // & WriteCore need to do different things. - return -1; - } - - // See code:#errorInvalidHandle in "private long SeekCore(long offset, SeekOrigin origin)". - if (hr == Win32Native.ERROR_INVALID_HANDLE) - _handle.Dispose(); - - return -1; - } - else - hr = 0; - return numBytesWritten; - } - - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [SecuritySafeCritical] - public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() or BeginRead() which a subclass might have overriden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/BeginRead) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.ReadAsync(buffer, offset, count, cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled<int>(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // If async IO is not supported on this platform or - // if this FileStream was not opened with FileOptions.Asynchronous. - if (!_isAsync) - return base.ReadAsync(buffer, offset, count, cancellationToken); - - var readTask = new FileStreamReadWriteTask<int>(cancellationToken); - var endReadTask = s_endReadTask; - if (endReadTask == null) s_endReadTask = endReadTask = EndReadTask; // benign initialization race condition - readTask._asyncResult = BeginReadAsync(buffer, offset, count, endReadTask, readTask); - - if (readTask._asyncResult.IsAsync && cancellationToken.CanBeCanceled) - { - var cancelReadHandler = s_cancelReadHandler; - if (cancelReadHandler == null) s_cancelReadHandler = cancelReadHandler = CancelTask<int>; // benign initialization race condition - readTask._registration = cancellationToken.Register(cancelReadHandler, readTask); - - // In case the task is completed right before we register the cancellation callback. - if (readTask._asyncResult.IsCompleted) - readTask._registration.Dispose(); - } - - return readTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [SecuritySafeCritical] - public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - offset < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Write() or BeginWrite() which a subclass might have overriden. - // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Write/BeginWrite) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.WriteAsync(buffer, offset, count, cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // If async IO is not supported on this platform or - // if this FileStream was not opened with FileOptions.Asynchronous. - if (!_isAsync) - return base.WriteAsync(buffer, offset, count, cancellationToken); - - var writeTask = new FileStreamReadWriteTask<VoidTaskResult>(cancellationToken); - var endWriteTask = s_endWriteTask; - if (endWriteTask == null) s_endWriteTask = endWriteTask = EndWriteTask; // benign initialization race condition - writeTask._asyncResult = BeginWriteAsync(buffer, offset, count, endWriteTask, writeTask); - - if (writeTask._asyncResult.IsAsync && cancellationToken.CanBeCanceled) - { - var cancelWriteHandler = s_cancelWriteHandler; - if (cancelWriteHandler == null) s_cancelWriteHandler = cancelWriteHandler = CancelTask<VoidTaskResult>; // benign initialization race condition - writeTask._registration = cancellationToken.Register(cancelWriteHandler, writeTask); - - // In case the task is completed right before we register the cancellation callback. - if (writeTask._asyncResult.IsCompleted) - writeTask._registration.Dispose(); - } - - return writeTask; - } - - // The task instance returned from ReadAsync and WriteAsync. - // Also stores all of the state necessary for those calls to avoid closures and extraneous delegate allocations. - private sealed class FileStreamReadWriteTask<T> : Task<T> - { - internal CancellationToken _cancellationToken; - internal CancellationTokenRegistration _registration; - internal FileStreamAsyncResult _asyncResult; // initialized after Begin call completes - - internal FileStreamReadWriteTask(CancellationToken cancellationToken) : base() - { - _cancellationToken = cancellationToken; - } - } - - // Cancellation callback for both ReadAsync and WriteAsync. - [SecuritySafeCritical] - private static void CancelTask<T>(object state) - { - var task = state as FileStreamReadWriteTask<T>; - Contract.Assert(task != null); - FileStreamAsyncResult asyncResult = task._asyncResult; - - // This method is used as both the completion callback and the cancellation callback. - // We should try to cancel the operation if this is running as the completion callback - // or if cancellation is not applicable: - // 1. asyncResult is not a FileStreamAsyncResult - // 2. asyncResult.IsAsync is false: asyncResult is a "synchronous" FileStreamAsyncResult. - // 3. The asyncResult is completed: this should never happen. - Contract.Assert((!asyncResult.IsWrite && typeof(T) == typeof(int)) || - (asyncResult.IsWrite && typeof(T) == typeof(VoidTaskResult))); - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsAsync); - - try - { - // Cancel the overlapped read and set the task to cancelled state. - if (!asyncResult.IsCompleted) - asyncResult.Cancel(); - } - catch (Exception ex) - { - task.TrySetException(ex); - } - } - - // Completion callback for ReadAsync - [SecuritySafeCritical] - private static void EndReadTask(IAsyncResult iar) - { - FileStreamAsyncResult asyncResult = iar as FileStreamAsyncResult; - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsCompleted, "How can we end up in the completion callback if the IAsyncResult is not completed?"); - - var readTask = asyncResult.AsyncState as FileStreamReadWriteTask<int>; - Contract.Assert(readTask != null); - - try - { - if (asyncResult.IsAsync) - { - asyncResult.ReleaseNativeResource(); - - // release the resource held by CancellationTokenRegistration - readTask._registration.Dispose(); - } - - if (asyncResult.ErrorCode == Win32Native.ERROR_OPERATION_ABORTED) - { - var cancellationToken = readTask._cancellationToken; - Contract.Assert(cancellationToken.IsCancellationRequested, "How can the IO operation be aborted if cancellation was not requested?"); - readTask.TrySetCanceled(cancellationToken); - } - else - readTask.TrySetResult(asyncResult.NumBytesRead); - } - catch (Exception ex) - { - readTask.TrySetException(ex); - } - } - - // Completion callback for WriteAsync - [SecuritySafeCritical] - private static void EndWriteTask(IAsyncResult iar) - { - var asyncResult = iar as FileStreamAsyncResult; - Contract.Assert(asyncResult != null); - Contract.Assert(asyncResult.IsCompleted, "How can we end up in the completion callback if the IAsyncResult is not completed?"); - - var writeTask = iar.AsyncState as FileStreamReadWriteTask<VoidTaskResult>; - Contract.Assert(writeTask != null); - - try - { - if (asyncResult.IsAsync) - { - asyncResult.ReleaseNativeResource(); - - // release the resource held by CancellationTokenRegistration - writeTask._registration.Dispose(); - } - - if (asyncResult.ErrorCode == Win32Native.ERROR_OPERATION_ABORTED) - { - var cancellationToken = writeTask._cancellationToken; - Contract.Assert(cancellationToken.IsCancellationRequested, "How can the IO operation be aborted if cancellation was not requested?"); - writeTask.TrySetCanceled(cancellationToken); - } - else - writeTask.TrySetResult(default(VoidTaskResult)); - } - catch (Exception ex) - { - writeTask.TrySetException(ex); - } - } - - // Unlike Flush(), FlushAsync() always flushes to disk. This is intentional. - // Legend is that we chose not to flush the OS file buffers in Flush() in fear of - // perf problems with frequent, long running FlushFileBuffers() calls. But we don't - // have that problem with FlushAsync() because we will call FlushFileBuffers() in the background. - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - [System.Security.SecuritySafeCritical] - public override Task FlushAsync(CancellationToken cancellationToken) - { - // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Flush() which a subclass might have overriden. To be safe - // we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Flush) when we are not sure. - if (this.GetType() != typeof(FileStream)) - return base.FlushAsync(cancellationToken); - - if (cancellationToken.IsCancellationRequested) - return Task.FromCanceled(cancellationToken); - - if (_handle.IsClosed) - __Error.FileNotOpen(); - - // The always synchronous data transfer between the OS and the internal buffer is intentional - // because this is needed to allow concurrent async IO requests. Concurrent data transfer - // between the OS and the internal buffer will result in race conditions. Since FlushWrite and - // FlushRead modify internal state of the stream and transfer data between the OS and the - // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers - // asynchronously because it doesn't modify any internal state of the stream and is potentially - // a long running process. - try - { - FlushInternalBuffer(); - } - catch (Exception e) - { - return Task.FromException(e); - } - - if (CanWrite) - return Task.Factory.StartNew( - state => ((FileStream)state).FlushOSBuffer(), - this, - cancellationToken, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.Default); - else - return Task.CompletedTask; - } - - } -} diff --git a/src/mscorlib/src/System/IO/FileSystemEnumerable.cs b/src/mscorlib/src/System/IO/FileSystemEnumerable.cs index c2e603c06a..f861805ccf 100644 --- a/src/mscorlib/src/System/IO/FileSystemEnumerable.cs +++ b/src/mscorlib/src/System/IO/FileSystemEnumerable.cs @@ -12,17 +12,14 @@ ** ===========================================================*/ -using System; using System.Collections; using System.Collections.Generic; -using System.Security; -using System.Security.Permissions; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; -using System.Text; using System.Runtime.InteropServices; using System.Globalization; using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading; @@ -158,7 +155,6 @@ namespace System.IO // For all the dirs/files returned, demands path discovery permission for their parent folders internal class FileSystemEnumerableIterator<TSource> : Iterator<TSource> { - private const int STATE_INIT = 1; private const int STATE_SEARCH_NEXT_DIR = 2; private const int STATE_FIND_NEXT_FILE = 3; @@ -168,9 +164,7 @@ namespace System.IO private List<Directory.SearchData> searchStack; private Directory.SearchData searchData; private String searchCriteria; - [System.Security.SecurityCritical] SafeFindHandle _hnd = null; - bool needsParentPathDiscoveryDemand; // empty means we know in advance that we won’t find any search results, which can happen if: // 1. we don’t have a search pattern @@ -185,9 +179,7 @@ namespace System.IO private String fullPath; private String normalizedSearchPath; private int oldMode; - private bool _checkHost; - [System.Security.SecuritySafeCritical] internal FileSystemEnumerableIterator(String path, String originalUserPath, String searchPattern, SearchOption searchOption, SearchResultHandler<TSource> resultHandler, bool checkHost) { Contract.Requires(path != null); @@ -211,30 +203,10 @@ namespace System.IO _resultHandler = resultHandler; this.searchOption = searchOption; - fullPath = Path.GetFullPathInternal(path); + fullPath = Path.GetFullPath(path); String fullSearchString = GetFullSearchString(fullPath, normalizedSearchPattern); normalizedSearchPath = Path.GetDirectoryName(fullSearchString); - // permission demands - String[] demandPaths = new String[2]; - // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters - demandPaths[0] = Directory.GetDemandDir(fullPath, true); - // For filters like foo\*.cs we need to verify if the directory foo is not denied access. - // Do a demand on the combined path so that we can fail early in case of deny - demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true); - _checkHost = checkHost; -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]); - state1.EnsureState(); - FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]); - state2.EnsureState(); - } -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); -#endif - // normalize search criteria searchCriteria = GetNormalizedSearchCriteria(fullSearchString, normalizedSearchPath); @@ -254,13 +226,12 @@ namespace System.IO } - [System.Security.SecurityCritical] private void CommonInit() { - Contract.Assert(searchCriteria != null && searchData != null, "searchCriteria and searchData should be initialized"); + Debug.Assert(searchCriteria != null && searchData != null, "searchCriteria and searchData should be initialized"); // Execute searchCriteria against the current directory - String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria); + String searchPath = Path.Combine(searchData.fullPath, searchCriteria); Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); @@ -307,8 +278,7 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] - private FileSystemEnumerableIterator(String fullPath, String normalizedSearchPath, String searchCriteria, String userPath, SearchOption searchOption, SearchResultHandler<TSource> resultHandler, bool checkHost) + private FileSystemEnumerableIterator(String fullPath, String normalizedSearchPath, String searchCriteria, String userPath, SearchOption searchOption, SearchResultHandler<TSource> resultHandler) { this.fullPath = fullPath; this.normalizedSearchPath = normalizedSearchPath; @@ -316,30 +286,11 @@ namespace System.IO this._resultHandler = resultHandler; this.userPath = userPath; this.searchOption = searchOption; - this._checkHost = checkHost; searchStack = new List<Directory.SearchData>(); if (searchCriteria != null) { - // permission demands - String[] demandPaths = new String[2]; - // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters - demandPaths[0] = Directory.GetDemandDir(fullPath, true); - // For filters like foo\*.cs we need to verify if the directory foo is not denied access. - // Do a demand on the combined path so that we can fail early in case of deny - demandPaths[1] = Directory.GetDemandDir(normalizedSearchPath, true); -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state1 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[0]); - state1.EnsureState(); - FileSecurityState state2 = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandPaths[1]); - state2.EnsureState(); - } -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); -#endif searchData = new Directory.SearchData(normalizedSearchPath, userPath, searchOption); CommonInit(); } @@ -351,10 +302,9 @@ namespace System.IO protected override Iterator<TSource> Clone() { - return new FileSystemEnumerableIterator<TSource>(fullPath, normalizedSearchPath, searchCriteria, userPath, searchOption, _resultHandler, _checkHost); + return new FileSystemEnumerableIterator<TSource>(fullPath, normalizedSearchPath, searchCriteria, userPath, searchOption, _resultHandler); } - [System.Security.SecuritySafeCritical] protected override void Dispose(bool disposing) { try @@ -371,7 +321,6 @@ namespace System.IO } } - [System.Security.SecuritySafeCritical] public override bool MoveNext() { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); @@ -404,19 +353,19 @@ namespace System.IO } case STATE_SEARCH_NEXT_DIR: { - Contract.Assert(searchData.searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly"); + Debug.Assert(searchData.searchOption != SearchOption.TopDirectoryOnly, "should not reach this code path if searchOption == TopDirectoryOnly"); // Traverse directory structure. We need to get '*' while (searchStack.Count > 0) { searchData = searchStack[0]; - Contract.Assert((searchData.fullPath != null), "fullpath can't be null!"); + Debug.Assert((searchData.fullPath != null), "fullpath can't be null!"); searchStack.RemoveAt(0); // Traverse the subdirs AddSearchableDirsToStack(searchData); // Execute searchCriteria against the current directory - String searchPath = Path.InternalCombine(searchData.fullPath, searchCriteria); + String searchPath = Path.Combine(searchData.fullPath, searchCriteria); // Open a Find handle _hnd = Win32Native.FindFirstFile(searchPath, data); @@ -431,15 +380,9 @@ namespace System.IO } state = STATE_FIND_NEXT_FILE; - needsParentPathDiscoveryDemand = true; SearchResult searchResult = CreateSearchResult(searchData, data); if (_resultHandler.IsResultIncluded(searchResult)) { - if (needsParentPathDiscoveryDemand) - { - DoDemand(searchData.fullPath); - needsParentPathDiscoveryDemand = false; - } current = _resultHandler.CreateObject(searchResult); return true; } @@ -461,11 +404,6 @@ namespace System.IO SearchResult searchResult = CreateSearchResult(searchData, data); if (_resultHandler.IsResultIncluded(searchResult)) { - if (needsParentPathDiscoveryDemand) - { - DoDemand(searchData.fullPath); - needsParentPathDiscoveryDemand = false; - } current = _resultHandler.CreateObject(searchResult); return true; } @@ -506,27 +444,24 @@ namespace System.IO return false; } - [System.Security.SecurityCritical] private SearchResult CreateSearchResult(Directory.SearchData localSearchData, Win32Native.WIN32_FIND_DATA findData) { - String userPathFinal = Path.InternalCombine(localSearchData.userPath, findData.cFileName); - String fullPathFinal = Path.InternalCombine(localSearchData.fullPath, findData.cFileName); + String userPathFinal = Path.Combine(localSearchData.userPath, findData.cFileName); + String fullPathFinal = Path.Combine(localSearchData.fullPath, findData.cFileName); return new SearchResult(fullPathFinal, userPathFinal, findData); } - [System.Security.SecurityCritical] private void HandleError(int hr, String path) { Dispose(); __Error.WinIOError(hr, path); } - [System.Security.SecurityCritical] // auto-generated private void AddSearchableDirsToStack(Directory.SearchData localSearchData) { Contract.Requires(localSearchData != null); - String searchPath = Path.InternalCombine(localSearchData.fullPath, "*"); + String searchPath = Path.Combine(localSearchData.fullPath, "*"); SafeFindHandle hnd = null; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); try @@ -553,8 +488,8 @@ namespace System.IO { if (FileSystemEnumerableHelpers.IsDir(data)) { - String tempFullPath = Path.InternalCombine(localSearchData.fullPath, data.cFileName); - String tempUserPath = Path.InternalCombine(localSearchData.userPath, data.cFileName); + String tempFullPath = Path.Combine(localSearchData.fullPath, data.cFileName); + String tempUserPath = Path.Combine(localSearchData.userPath, data.cFileName); SearchOption option = localSearchData.searchOption; @@ -578,28 +513,12 @@ namespace System.IO } } - [System.Security.SecurityCritical] - internal void DoDemand(String fullPathToDemand) - { -#if FEATURE_CORECLR - if(_checkHost) { - String demandDir = Directory.GetDemandDir(fullPathToDemand, true); - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir); - state.EnsureState(); - } -#else - String demandDir = Directory.GetDemandDir(fullPathToDemand, true); - String[] demandPaths = new String[] { demandDir }; - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); -#endif - } - private static String NormalizeSearchPattern(String searchPattern) { Contract.Requires(searchPattern != null); - // Win32 normalization trims only U+0020. - String tempSearchPattern = searchPattern.TrimEnd(Path.TrimEndChars); + // Win32 normalization trims only U+0020. + String tempSearchPattern = searchPattern.TrimEnd(PathInternal.s_trimEndChars); // Make this corner case more useful, like dir if (tempSearchPattern.Equals(".")) @@ -607,7 +526,7 @@ namespace System.IO tempSearchPattern = "*"; } - Path.CheckSearchPattern(tempSearchPattern); + PathInternal.CheckSearchPattern(tempSearchPattern); return tempSearchPattern; } @@ -619,14 +538,14 @@ namespace System.IO String searchCriteria = null; char lastChar = fullPathMod[fullPathMod.Length - 1]; - if (Path.IsDirectorySeparator(lastChar)) + if (PathInternal.IsDirectorySeparator(lastChar)) { // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\ searchCriteria = fullSearchString.Substring(fullPathMod.Length); } else { - Contract.Assert(fullSearchString.Length > fullPathMod.Length); + Debug.Assert(fullSearchString.Length > fullPathMod.Length); searchCriteria = fullSearchString.Substring(fullPathMod.Length + 1); } return searchCriteria; @@ -637,11 +556,11 @@ namespace System.IO Contract.Requires(fullPath != null); Contract.Requires(searchPattern != null); - String tempStr = Path.InternalCombine(fullPath, searchPattern); + String tempStr = Path.Combine(fullPath, searchPattern); // If path ends in a trailing slash (\), append a * or we'll get a "Cannot find the file specified" exception char lastChar = tempStr[tempStr.Length - 1]; - if (Path.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar) + if (PathInternal.IsDirectorySeparator(lastChar) || lastChar == Path.VolumeSeparatorChar) { tempStr = tempStr + '*'; } @@ -653,10 +572,8 @@ namespace System.IO internal abstract class SearchResultHandler<TSource> { - [System.Security.SecurityCritical] internal abstract bool IsResultIncluded(SearchResult result); - [System.Security.SecurityCritical] internal abstract TSource CreateObject(SearchResult result); } @@ -672,16 +589,14 @@ namespace System.IO _includeDirs = includeDirs; } - [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { bool includeFile = _includeFiles && FileSystemEnumerableHelpers.IsFile(result.FindData); bool includeDir = _includeDirs && FileSystemEnumerableHelpers.IsDir(result.FindData); - Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); + Debug.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); return (includeFile || includeDir); } - [System.Security.SecurityCritical] internal override String CreateObject(SearchResult result) { return result.UserPath; @@ -690,23 +605,14 @@ namespace System.IO internal class FileInfoResultHandler : SearchResultHandler<FileInfo> { - [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { return FileSystemEnumerableHelpers.IsFile(result.FindData); } - [System.Security.SecurityCritical] internal override FileInfo CreateObject(SearchResult result) { String name = result.FullPath; -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); - state.EnsureState(); -#else - String[] names = new String[] { name }; - new FileIOPermission(FileIOPermissionAccess.Read, names, false, false).Demand(); -#endif FileInfo fi = new FileInfo(name, false); fi.InitializeFrom(result.FindData); return fi; @@ -715,26 +621,14 @@ namespace System.IO internal class DirectoryInfoResultHandler : SearchResultHandler<DirectoryInfo> { - [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { return FileSystemEnumerableHelpers.IsDir(result.FindData); } - [System.Security.SecurityCritical] internal override DirectoryInfo CreateObject(SearchResult result) { - String name = result.FullPath; - String permissionName = name + "\\."; - -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); - state.EnsureState(); -#else - String[] permissionNames = new String[] { permissionName }; - new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand(); -#endif - DirectoryInfo di = new DirectoryInfo(name, false); + DirectoryInfo di = new DirectoryInfo(result.FullPath, false); di.InitializeFrom(result.FindData); return di; } @@ -743,17 +637,15 @@ namespace System.IO internal class FileSystemInfoResultHandler : SearchResultHandler<FileSystemInfo> { - [System.Security.SecurityCritical] internal override bool IsResultIncluded(SearchResult result) { bool includeFile = FileSystemEnumerableHelpers.IsFile(result.FindData); bool includeDir = FileSystemEnumerableHelpers.IsDir(result.FindData); - Contract.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); + Debug.Assert(!(includeFile && includeDir), result.FindData.cFileName + ": current item can't be both file and dir!"); return (includeDir || includeFile); } - [System.Security.SecurityCritical] internal override FileSystemInfo CreateObject(SearchResult result) { bool isFile = FileSystemEnumerableHelpers.IsFile(result.FindData); @@ -761,33 +653,14 @@ namespace System.IO if (isDir) { - String name = result.FullPath; - String permissionName = name + "\\."; - -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, permissionName); - state.EnsureState(); -#else - String[] permissionNames = new String[] { permissionName }; - new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand(); -#endif - DirectoryInfo di = new DirectoryInfo(name, false); + DirectoryInfo di = new DirectoryInfo(result.FullPath, false); di.InitializeFrom(result.FindData); return di; } else { Contract.Assert(isFile); - String name = result.FullPath; - -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, name); - state.EnsureState(); -#else - String[] names = new String[] { name }; - new FileIOPermission(FileIOPermissionAccess.Read, names, false, false).Demand(); -#endif - FileInfo fi = new FileInfo(name, false); + FileInfo fi = new FileInfo(result.FullPath, false); fi.InitializeFrom(result.FindData); return fi; } @@ -799,10 +672,8 @@ namespace System.IO { private String fullPath; // fully-qualifed path private String userPath; // user-specified path - [System.Security.SecurityCritical] private Win32Native.WIN32_FIND_DATA findData; - [System.Security.SecurityCritical] internal SearchResult(String fullPath, String userPath, Win32Native.WIN32_FIND_DATA findData) { Contract.Requires(fullPath != null); @@ -825,15 +696,12 @@ namespace System.IO internal Win32Native.WIN32_FIND_DATA FindData { - [System.Security.SecurityCritical] get { return findData; } } - } internal static class FileSystemEnumerableHelpers { - [System.Security.SecurityCritical] // auto-generated internal static bool IsDir(Win32Native.WIN32_FIND_DATA data) { // Don't add "." nor ".." @@ -841,7 +709,6 @@ namespace System.IO && !data.cFileName.Equals(".") && !data.cFileName.Equals(".."); } - [System.Security.SecurityCritical] // auto-generated internal static bool IsFile(Win32Native.WIN32_FIND_DATA data) { return 0 == (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY); diff --git a/src/mscorlib/src/System/IO/FileSystemInfo.cs b/src/mscorlib/src/System/IO/FileSystemInfo.cs index 7a17a417af..94cd531b07 100644 --- a/src/mscorlib/src/System/IO/FileSystemInfo.cs +++ b/src/mscorlib/src/System/IO/FileSystemInfo.cs @@ -2,43 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** -** -** Purpose: -** -** -===========================================================*/ - -using System; -using System.Collections; -using System.Security; -using System.Security.Permissions; using Microsoft.Win32; -using System.Text; using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Runtime.Versioning; using System.Diagnostics.Contracts; -namespace System.IO { +namespace System.IO +{ #if FEATURE_SERIALIZATION [Serializable] #endif -#if !FEATURE_CORECLR - [FileIOPermissionAttribute(SecurityAction.InheritanceDemand,Unrestricted=true)] -#endif [ComVisible(true)] -#if FEATURE_REMOTING public abstract class FileSystemInfo : MarshalByRefObject, ISerializable { -#else // FEATURE_REMOTING - public abstract class FileSystemInfo : ISerializable { -#endif //FEATURE_REMOTING - [System.Security.SecurityCritical] // auto-generated internal Win32Native.WIN32_FILE_ATTRIBUTE_DATA _data; // Cache the file information internal int _dataInitialised = -1; // We use this field in conjunction with the Refresh methods, if we succeed // we store a zero, on failure we store the HResult in it so that we can @@ -51,13 +27,6 @@ namespace System.IO { protected String OriginalPath; // path passed in by the user private String _displayPath = ""; // path that can be displayed to the user -#if FEATURE_CORECLR -#if FEATURE_CORESYSTEM - [System.Security.SecurityCritical] -#else - [System.Security.SecuritySafeCritical] -#endif //FEATURE_CORESYSTEM -#endif protected FileSystemInfo() { } @@ -65,19 +34,18 @@ namespace System.IO { protected FileSystemInfo(SerializationInfo info, StreamingContext context) { if (info == null) - throw new ArgumentNullException("info"); + throw new ArgumentNullException(nameof(info)); Contract.EndContractBlock(); - + // Must use V1 field names here, since V1 didn't implement // ISerializable. - FullPath = Path.GetFullPathInternal(info.GetString("FullPath")); + FullPath = Path.GetFullPath(info.GetString("FullPath")); OriginalPath = info.GetString("OriginalPath"); // Lazily initialize the file attributes. _dataInitialised = -1; } - [System.Security.SecurityCritical] internal void InitializeFrom(Win32Native.WIN32_FIND_DATA findData) { _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); @@ -87,37 +55,8 @@ namespace System.IO { // Full path of the direcory/file public virtual String FullName { - [System.Security.SecuritySafeCritical] get { - String demandDir; - if (this is DirectoryInfo) - demandDir = Directory.GetDemandDir(FullPath, true); - else - demandDir = FullPath; -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir); - sourceState.EnsureState(); -#else - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandDir).Demand(); -#endif - return FullPath; - } - } - - internal virtual String UnsafeGetFullName - { - [System.Security.SecurityCritical] - get - { - String demandDir; - if (this is DirectoryInfo) - demandDir = Directory.GetDemandDir(FullPath, true); - else - demandDir = FullPath; -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandDir).Demand(); -#endif return FullPath; } } @@ -160,21 +99,11 @@ namespace System.IO { // depends on the security check in get_CreationTimeUtc return CreationTimeUtc.ToLocalTime(); } - - set { - CreationTimeUtc = value.ToUniversalTime(); - } } [ComVisible(false)] public DateTime CreationTimeUtc { - [System.Security.SecuritySafeCritical] get { -#if FEATURE_CORECLR - // get_CreationTime also depends on this security check - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath); - sourceState.EnsureState(); -#endif if (_dataInitialised == -1) { _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); @@ -187,19 +116,10 @@ namespace System.IO { return DateTime.FromFileTimeUtc(fileTime); } - - set { - if (this is DirectoryInfo) - Directory.SetCreationTimeUtc(FullPath,value); - else - File.SetCreationTimeUtc(FullPath,value); - _dataInitialised = -1; - } } - public DateTime LastAccessTime - { + { get { // depends on the security check in get_LastAccessTimeUtc return LastAccessTimeUtc.ToLocalTime(); @@ -211,13 +131,7 @@ namespace System.IO { [ComVisible(false)] public DateTime LastAccessTimeUtc { - [System.Security.SecuritySafeCritical] get { -#if FEATURE_CORECLR - // get_LastAccessTime also depends on this security check - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath); - sourceState.EnsureState(); -#endif if (_dataInitialised == -1) { _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); @@ -228,15 +142,9 @@ namespace System.IO { long fileTime = ((long)_data.ftLastAccessTimeHigh << 32) | _data.ftLastAccessTimeLow; return DateTime.FromFileTimeUtc(fileTime); - } set { - if (this is DirectoryInfo) - Directory.SetLastAccessTimeUtc(FullPath,value); - else - File.SetLastAccessTimeUtc(FullPath,value); - _dataInitialised = -1; } } @@ -254,13 +162,7 @@ namespace System.IO { [ComVisible(false)] public DateTime LastWriteTimeUtc { - [System.Security.SecuritySafeCritical] get { -#if FEATURE_CORECLR - // get_LastWriteTime also depends on this security check - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath); - sourceState.EnsureState(); -#endif if (_dataInitialised == -1) { _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); @@ -275,28 +177,17 @@ namespace System.IO { } set { - if (this is DirectoryInfo) - Directory.SetLastWriteTimeUtc(FullPath,value); - else - File.SetLastWriteTimeUtc(FullPath,value); - _dataInitialised = -1; } } - [System.Security.SecuritySafeCritical] // auto-generated public void Refresh() { _dataInitialised = File.FillAttributeInfo(FullPath, ref _data, false, false); } public FileAttributes Attributes { - [System.Security.SecuritySafeCritical] get { -#if FEATURE_CORECLR - FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath); - sourceState.EnsureState(); -#endif if (_dataInitialised == -1) { _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); // Call refresh to intialise the data @@ -307,15 +198,8 @@ namespace System.IO { return (FileAttributes) _data.fileAttributes; } -#if FEATURE_CORECLR - [System.Security.SecurityCritical] // auto-generated -#else - [System.Security.SecuritySafeCritical] -#endif + set { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.Write, FullPath).Demand(); -#endif bool r = Win32Native.SetFileAttributes(FullPath, (int) value); if (!r) { int hr = Marshal.GetLastWin32Error(); @@ -334,14 +218,9 @@ namespace System.IO { } } - [System.Security.SecurityCritical] // auto-generated_required [ComVisible(false)] public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, FullPath).Demand(); -#endif - info.AddValue("OriginalPath", OriginalPath, typeof(String)); info.AddValue("FullPath", FullPath, typeof(String)); } @@ -357,5 +236,5 @@ namespace System.IO { _displayPath = value; } } - } + } } diff --git a/src/mscorlib/src/System/IO/LongPathHelper.cs b/src/mscorlib/src/System/IO/LongPathHelper.cs deleted file mode 100644 index 9746fdc0aa..0000000000 --- a/src/mscorlib/src/System/IO/LongPathHelper.cs +++ /dev/null @@ -1,521 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Win32; - -namespace System.IO -{ - /// <summary> - /// Wrapper to help with path normalization. - /// </summary> - internal class LongPathHelper - { - // Can't be over 8.3 and be a short name - private const int MaxShortName = 12; - - private const char LastAnsi = (char)255; - private const char Delete = (char)127; - - [ThreadStatic] - private static StringBuffer t_fullPathBuffer; - - /// <summary> - /// Normalize the given path. - /// </summary> - /// <remarks> - /// Normalizes via Win32 GetFullPathName(). It will also trim all "typical" whitespace at the end of the path (see s_trimEndChars). Will also trim initial - /// spaces if the path is determined to be rooted. - /// - /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt) - /// </remarks> - /// <param name="path">Path to normalize</param> - /// <param name="checkInvalidCharacters">True to check for invalid characters</param> - /// <param name="expandShortPaths">Attempt to expand short paths if true</param> - /// <exception cref="ArgumentException">Thrown if the path is an illegal UNC (does not contain a full server/share) or contains illegal characters.</exception> - /// <exception cref="PathTooLongException">Thrown if the path or a path segment exceeds the filesystem limits.</exception> - /// <exception cref="FileNotFoundException">Thrown if Windows returns ERROR_FILE_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception> - /// <exception cref="DirectoryNotFoundException">Thrown if Windows returns ERROR_PATH_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception> - /// <exception cref="UnauthorizedAccessException">Thrown if Windows returns ERROR_ACCESS_DENIED. (See Win32Marshal.GetExceptionForWin32Error)</exception> - /// <exception cref="IOException">Thrown if Windows returns an error that doesn't map to the above. (See Win32Marshal.GetExceptionForWin32Error)</exception> - /// <returns>Normalized path</returns> - [System.Security.SecurityCritical] - unsafe internal static string Normalize(string path, uint maxPathLength, bool checkInvalidCharacters, bool expandShortPaths) - { - // Get the full path - StringBuffer fullPath = t_fullPathBuffer ?? (t_fullPathBuffer = new StringBuffer(PathInternal.MaxShortPath)); - try - { - GetFullPathName(path, fullPath); - - // Trim whitespace off the end of the string. Win32 normalization trims only U+0020. - fullPath.TrimEnd(Path.TrimEndChars); - - if (fullPath.Length >= maxPathLength) - { - // Fullpath is genuinely too long - throw new PathTooLongException(); - } - - // Checking path validity used to happen before getting the full path name. To avoid additional input allocation - // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that - // used to get kicked back (notably segments with invalid characters might get removed via ".."). - // - // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to - // expand short file names. - - // Scan the path for: - // - // - Illegal path characters. - // - Invalid UNC paths like \\, \\server, \\server\. - // - Segments that are too long (over MaxComponentLength) - - // As the path could be > 60K, we'll combine the validity scan. None of these checks are performed by the Win32 - // GetFullPathName() API. - - bool possibleShortPath = false; - bool foundTilde = false; - - // We can get UNCs as device paths through this code (e.g. \\.\UNC\), we won't validate them as there isn't - // an easy way to normalize without extensive cost (we'd have to hunt down the canonical name for any device - // path that contains UNC or to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\, - // \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc. - bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\'; - bool isDevice = PathInternal.IsDevice(fullPath); - bool possibleBadUnc = specialPath && !isDevice; - uint index = specialPath ? 2u : 0; - uint lastSeparator = specialPath ? 1u : 0; - uint segmentLength; - char* start = fullPath.CharPointer; - char current; - - while (index < fullPath.Length) - { - current = start[index]; - - // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~' - if (current < '?' || current == '\\' || current == '|' || current == '~') - { - switch (current) - { - case '|': - case '>': - case '<': - case '\"': - if (checkInvalidCharacters) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); - // No point in expanding a bad path - foundTilde = false; - break; - case '~': - foundTilde = true; - break; - case '\\': - segmentLength = index - lastSeparator - 1; - if (segmentLength > (uint)PathInternal.MaxComponentLength) - throw new PathTooLongException(); - lastSeparator = index; - - if (foundTilde) - { - if (segmentLength <= MaxShortName) - { - // Possibly a short path. - possibleShortPath = true; - } - - foundTilde = false; - } - - if (possibleBadUnc) - { - // If we're at the end of the path and this is the first separator, we're missing the share. - // Otherwise we're good, so ignore UNC tracking from here. - if (index == fullPath.Length - 1) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); - else - possibleBadUnc = false; - } - - break; - - default: - if (checkInvalidCharacters && current < ' ') throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); - break; - } - } - - index++; - } - - if (possibleBadUnc) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); - - segmentLength = fullPath.Length - lastSeparator - 1; - if (segmentLength > (uint)PathInternal.MaxComponentLength) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - if (foundTilde && segmentLength <= MaxShortName) - possibleShortPath = true; - - // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but - // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide. - if (expandShortPaths && possibleShortPath) - { - return TryExpandShortFileName(fullPath, originalPath: path); - } - else - { - if (fullPath.Length == (uint)path.Length && fullPath.StartsWith(path)) - { - // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder. - return path; - } - else - { - return fullPath.ToString(); - } - } - } - finally - { - // Clear the buffer - fullPath.Free(); - } - } - - [System.Security.SecurityCritical] - unsafe private static void GetFullPathName(string path, StringBuffer fullPath) - { - // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as - // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here. - Contract.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path)); - - // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\" - int startIndex = PathInternal.PathStartSkip(path); - - fixed (char* pathStart = path) - { - uint result = 0; - while ((result = Win32Native.GetFullPathNameW(pathStart + startIndex, fullPath.CharCapacity, fullPath.GetHandle(), IntPtr.Zero)) > fullPath.CharCapacity) - { - // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity. - fullPath.EnsureCharCapacity(result); - } - - if (result == 0) - { - // Failure, get the error and throw - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == 0) - errorCode = Win32Native.ERROR_BAD_PATHNAME; - __Error.WinIOError(errorCode, path); - } - - fullPath.Length = result; - } - } - - [System.Security.SecurityCritical] - unsafe internal static string GetLongPathName(StringBuffer path) - { - using (StringBuffer outputBuffer = new StringBuffer(path.Length)) - { - uint result = 0; - while ((result = Win32Native.GetLongPathNameW(path.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity)) > outputBuffer.CharCapacity) - { - // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity. - outputBuffer.EnsureCharCapacity(result); - } - - if (result == 0) - { - // Failure, get the error and throw - GetErrorAndThrow(path.ToString()); - } - - outputBuffer.Length = result; - return outputBuffer.ToString(); - } - } - - [System.Security.SecurityCritical] - unsafe internal static string GetLongPathName(string path) - { - using (StringBuffer outputBuffer = new StringBuffer((uint)path.Length)) - { - uint result = 0; - while ((result = Win32Native.GetLongPathNameW(path, outputBuffer.GetHandle(), outputBuffer.CharCapacity)) > outputBuffer.CharCapacity) - { - // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity. - outputBuffer.EnsureCharCapacity(result); - } - - if (result == 0) - { - // Failure, get the error and throw - GetErrorAndThrow(path); - } - - outputBuffer.Length = result; - return outputBuffer.ToString(); - } - } - - [System.Security.SecurityCritical] - private static void GetErrorAndThrow(string path) - { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == 0) - errorCode = Win32Native.ERROR_BAD_PATHNAME; - __Error.WinIOError(errorCode, path); - } - - // It is significantly more complicated to get the long path with minimal allocations if we're injecting the extended dos path prefix. The implicit version - // should match up with what is in CoreFx System.Runtime.Extensions. -#if !FEATURE_IMPLICIT_LONGPATH - [System.Security.SecuritySafeCritical] - private unsafe static string TryExpandShortFileName(StringBuffer outputBuffer, string originalPath) - { - // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To - // avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls. - - Contract.Assert(!PathInternal.IsPartiallyQualified(outputBuffer), "should have resolved by now"); - - using (StringBuffer inputBuffer = new StringBuffer(outputBuffer)) - { - bool success = false; - uint lastIndex = outputBuffer.Length - 1; - uint foundIndex = lastIndex; - uint rootLength = PathInternal.GetRootLength(outputBuffer); - - while (!success) - { - uint result = Win32Native.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity); - - // Replace any temporary null we added - if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\'; - - if (result == 0) - { - // Look to see if we couldn't find the file - int error = Marshal.GetLastWin32Error(); - if (error != Win32Native.ERROR_FILE_NOT_FOUND && error != Win32Native.ERROR_PATH_NOT_FOUND) - { - // Some other failure, give up - break; - } - - // We couldn't find the path at the given index, start looking further back in the string. - foundIndex--; - - for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ; - if (foundIndex == rootLength) - { - // Can't trim the path back any further - break; - } - else - { - // Temporarily set a null in the string to get Windows to look further up the path - inputBuffer[foundIndex] = '\0'; - } - } - else if (result > outputBuffer.CharCapacity) - { - // Not enough space. The result count for this API does not include the null terminator. - outputBuffer.EnsureCharCapacity(result); - } - else - { - // Found the path - success = true; - outputBuffer.Length = result; - if (foundIndex < lastIndex) - { - // It was a partial find, put the non-existant part of the path back - outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex); - } - } - } - - StringBuffer bufferToUse = success ? outputBuffer : inputBuffer; - - if (bufferToUse.SubstringEquals(originalPath)) - { - // Use the original path to avoid allocating - return originalPath; - } - - return bufferToUse.ToString(); - } - } -#else // !FEATURE_IMPLICIT_LONGPATH - - private static uint GetInputBuffer(StringBuffer content, bool isDosUnc, out StringBuffer buffer) - { - uint length = content.Length; - - length += isDosUnc - ? (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength - : PathInternal.DevicePrefixLength; - - buffer = new StringBuffer(length); - - if (isDosUnc) - { - // Put the extended UNC prefix (\\?\UNC\) in front of the path - buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix); - - // Copy the source buffer over after the existing UNC prefix - content.CopyTo( - bufferIndex: PathInternal.UncPrefixLength, - destination: buffer, - destinationIndex: PathInternal.UncExtendedPrefixLength, - count: content.Length - PathInternal.UncPrefixLength); - - // Return the prefix difference - return (uint)PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength; - } - else - { - uint prefixSize = (uint)PathInternal.ExtendedPathPrefix.Length; - buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix); - content.CopyTo(bufferIndex: 0, destination: buffer, destinationIndex: prefixSize, count: content.Length); - return prefixSize; - } - } - - [System.Security.SecuritySafeCritical] - private static string TryExpandShortFileName(StringBuffer outputBuffer, string originalPath) - { - // We'll have one of a few cases by now (the normalized path will have already: - // - // 1. Dos path (C:\) - // 2. Dos UNC (\\Server\Share) - // 3. Dos device path (\\.\C:\, \\?\C:\) - // - // We want to put the extended syntax on the front if it doesn't already have it, which may mean switching from \\.\. - - uint rootLength = PathInternal.GetRootLength(outputBuffer); - bool isDevice = PathInternal.IsDevice(outputBuffer); - - StringBuffer inputBuffer = null; - bool isDosUnc = false; - uint rootDifference = 0; - bool wasDotDevice = false; - - // Add the extended prefix before expanding to allow growth over MAX_PATH - if (isDevice) - { - // We have one of the following (\\?\ or \\.\) - // We will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is). - inputBuffer = new StringBuffer(); - inputBuffer.Append(outputBuffer); - - if (outputBuffer[2] == '.') - { - wasDotDevice = true; - inputBuffer[2] = '?'; - } - } - else - { - // \\Server\Share, but not \\.\ or \\?\. - // We need to know this to be able to push \\?\UNC\ on if required - isDosUnc = outputBuffer.Length > 1 && outputBuffer[0] == '\\' && outputBuffer[1] == '\\' && !PathInternal.IsDevice(outputBuffer); - rootDifference = GetInputBuffer(outputBuffer, isDosUnc, out inputBuffer); - } - - rootLength += rootDifference; - uint inputLength = inputBuffer.Length; - - bool success = false; - uint foundIndex = inputBuffer.Length - 1; - - while (!success) - { - uint result = Win32Native.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity); - - // Replace any temporary null we added - if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\'; - - if (result == 0) - { - // Look to see if we couldn't find the file - int error = Marshal.GetLastWin32Error(); - if (error != Win32Native.ERROR_FILE_NOT_FOUND && error != Win32Native.ERROR_PATH_NOT_FOUND) - { - // Some other failure, give up - break; - } - - // We couldn't find the path at the given index, start looking further back in the string. - foundIndex--; - - for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ; - if (foundIndex == rootLength) - { - // Can't trim the path back any further - break; - } - else - { - // Temporarily set a null in the string to get Windows to look further up the path - inputBuffer[foundIndex] = '\0'; - } - } - else if (result > outputBuffer.CharCapacity) - { - // Not enough space. The result count for this API does not include the null terminator. - outputBuffer.EnsureCharCapacity(result); - result = Win32Native.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), outputBuffer.CharCapacity); - } - else - { - // Found the path - success = true; - outputBuffer.Length = result; - if (foundIndex < inputLength - 1) - { - // It was a partial find, put the non-existent part of the path back - outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex); - } - } - } - - // Strip out the prefix and return the string - StringBuffer bufferToUse = success ? outputBuffer : inputBuffer; - - // Switch back from \\?\ to \\.\ if necessary - if (wasDotDevice) - bufferToUse[2] = '.'; - - string returnValue = null; - - int newLength = (int)(bufferToUse.Length - rootDifference); - if (isDosUnc) - { - // Need to go from \\?\UNC\ to \\?\UN\\ - bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\'; - } - - // We now need to strip out any added characters at the front of the string - if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength)) - { - // Use the original path to avoid allocating - returnValue = originalPath; - } - else - { - returnValue = bufferToUse.Substring(rootDifference, newLength); - } - - inputBuffer.Dispose(); - return returnValue; - } -#endif // FEATURE_IMPLICIT_LONGPATH - } -}
\ No newline at end of file diff --git a/src/mscorlib/src/System/IO/MemoryStream.cs b/src/mscorlib/src/System/IO/MemoryStream.cs index edb583b9b5..bdddc83818 100644 --- a/src/mscorlib/src/System/IO/MemoryStream.cs +++ b/src/mscorlib/src/System/IO/MemoryStream.cs @@ -19,6 +19,7 @@ using System; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading; using System.Threading.Tasks; @@ -61,7 +62,7 @@ namespace System.IO { public MemoryStream(int capacity) { if (capacity < 0) { - throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity")); + throw new ArgumentOutOfRangeException(nameof(capacity), Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity")); } Contract.EndContractBlock(); @@ -79,7 +80,7 @@ namespace System.IO { } public MemoryStream(byte[] buffer, bool writable) { - if (buffer == null) throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + if (buffer == null) throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); Contract.EndContractBlock(); _buffer = buffer; _length = _capacity = buffer.Length; @@ -99,11 +100,11 @@ namespace System.IO { public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -180,7 +181,6 @@ namespace System.IO { public override void Flush() { } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task FlushAsync(CancellationToken cancellationToken) { @@ -259,7 +259,7 @@ namespace System.IO { if (n > count) n = count; if (n < 0) n = 0; - Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. _position += n; return n; } @@ -276,7 +276,7 @@ namespace System.IO { set { // Only update the capacity if the MS is expandable and the value is different than the current capacity. // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity - if (value < Length) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); + if (value < Length) throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity")); Contract.Ensures(_capacity - _origin == value); Contract.EndContractBlock(); @@ -312,25 +312,25 @@ namespace System.IO { } set { if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); Contract.Ensures(Position == value); Contract.EndContractBlock(); if (!_isOpen) __Error.StreamIsClosed(); if (value > MemStreamMaxLength) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); _position = _origin + (int)value; } } public override int Read([In, Out] byte[] buffer, int offset, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -342,7 +342,7 @@ namespace System.IO { if (n <= 0) return 0; - Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. + Debug.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1. if (n <= 8) { @@ -357,16 +357,15 @@ namespace System.IO { return n; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // contract validation copied from Read(...) @@ -379,7 +378,7 @@ namespace System.IO { { int n = Read(buffer, offset, count); var t = _lastReadTask; - Contract.Assert(t == null || t.Status == TaskStatus.RanToCompletion, + Debug.Assert(t == null || t.Status == TaskStatus.RanToCompletion, "Expected that a stored last task completed successfully"); return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n)); } @@ -402,36 +401,46 @@ namespace System.IO { return _buffer[_position++]; } + public override void CopyTo(Stream destination, int bufferSize) + { + // Since we did not originally override this method, validate the arguments + // the same way Stream does for back-compat. + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); - public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) { - - // This implementation offers beter performance compared to the base class version. - - // The parameter checks must be in sync with the base version: - if (destination == null) - throw new ArgumentNullException("destination"); + // If we have been inherited into a subclass, the following implementation could be incorrect + // since it does not call through to Read() which a subclass might have overridden. + // To be safe we will only use this implementation in cases where we know it is safe to do so, + // and delegate to our base class (which will call into Read) when we are not sure. + if (GetType() != typeof(MemoryStream)) + { + base.CopyTo(destination, bufferSize); + return; + } - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + int originalPosition = _position; - if (!CanRead && !CanWrite) - throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed")); + // Seek to the end of the MemoryStream. + int remaining = InternalEmulateRead(_length - originalPosition); - if (!destination.CanRead && !destination.CanWrite) - throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed")); + // If we were already at or past the end, there's no copying to do so just quit. + if (remaining > 0) + { + // Call Write() on the other Stream, using our internal buffer and avoiding any + // intermediary allocations. + destination.Write(_buffer, originalPosition, remaining); + } + } - if (!CanRead) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream")); + public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) { - if (!destination.CanWrite) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream")); + // This implementation offers beter performance compared to the base class version. - Contract.EndContractBlock(); + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); // If we have been inherited into a subclass, the following implementation could be incorrect - // since it does not call through to Read() or Write() which a subclass might have overriden. + // since it does not call through to ReadAsync() which a subclass might have overridden. // To be safe we will only use this implementation in cases where we know it is safe to do so, - // and delegate to our base class (which will call into Read/Write) when we are not sure. + // and delegate to our base class (which will call into ReadAsync) when we are not sure. if (this.GetType() != typeof(MemoryStream)) return base.CopyToAsync(destination, bufferSize, cancellationToken); @@ -467,7 +476,7 @@ namespace System.IO { if (!_isOpen) __Error.StreamIsClosed(); if (offset > MemStreamMaxLength) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); switch(loc) { case SeekOrigin.Begin: { int tempPosition = unchecked(_origin + (int)offset); @@ -494,7 +503,7 @@ namespace System.IO { throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin")); } - Contract.Assert(_position >= 0, "_position >= 0"); + Debug.Assert(_position >= 0, "_position >= 0"); return _position; } @@ -510,16 +519,16 @@ namespace System.IO { // public override void SetLength(long value) { if (value < 0 || value > Int32.MaxValue) { - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); } Contract.Ensures(_length - _origin == value); Contract.EndContractBlock(); EnsureWriteable(); // Origin wasn't publicly exposed above. - Contract.Assert(MemStreamMaxLength == Int32.MaxValue); // Check parameter validation logic in this method if this fails. + Debug.Assert(MemStreamMaxLength == Int32.MaxValue); // Check parameter validation logic in this method if this fails. if (value > (Int32.MaxValue - _origin)) { - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); } int newLength = _origin + (int)value; @@ -540,11 +549,11 @@ namespace System.IO { public override void Write(byte[] buffer, int offset, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -580,16 +589,15 @@ namespace System.IO { } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (buffer == null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // contract validation copied from Write(...) @@ -636,7 +644,7 @@ namespace System.IO { // Writes this MemoryStream to another stream. public virtual void WriteTo(Stream stream) { if (stream==null) - throw new ArgumentNullException("stream", Environment.GetResourceString("ArgumentNull_Stream")); + throw new ArgumentNullException(nameof(stream), Environment.GetResourceString("ArgumentNull_Stream")); Contract.EndContractBlock(); if (!_isOpen) __Error.StreamIsClosed(); diff --git a/src/mscorlib/src/System/IO/Path.cs b/src/mscorlib/src/System/IO/Path.cs deleted file mode 100644 index 4f7993633b..0000000000 --- a/src/mscorlib/src/System/IO/Path.cs +++ /dev/null @@ -1,1435 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** -** Purpose: A collection of path manipulation methods. -** -** -===========================================================*/ - -using System; -using System.Security.Permissions; -using Win32Native = Microsoft.Win32.Win32Native; -using System.Text; -using System.Runtime.InteropServices; -using System.Security; -#if FEATURE_LEGACYSURFACE -using System.Security.Cryptography; -#endif -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Runtime.Versioning; -using System.Diagnostics.Contracts; - -namespace System.IO { - // Provides methods for processing directory strings in an ideally - // cross-platform manner. Most of the methods don't do a complete - // full parsing (such as examining a UNC hostname), but they will - // handle most string operations. - [ComVisible(true)] - public static class Path - { - // Platform specific directory separator character. This is backslash - // ('\') on Windows and slash ('/') on Unix. - // -#if !PLATFORM_UNIX - public static readonly char DirectorySeparatorChar = '\\'; - internal const string DirectorySeparatorCharAsString = "\\"; -#else - public static readonly char DirectorySeparatorChar = '/'; - internal const string DirectorySeparatorCharAsString = "/"; -#endif // !PLATFORM_UNIX - - // Platform specific alternate directory separator character. - // There is only one directory separator char on Unix, - // so the same definition is used for both Unix and Windows. - public static readonly char AltDirectorySeparatorChar = '/'; - - // Platform specific volume separator character. This is colon (':') - // on Windows and MacOS, and slash ('/') on Unix. This is mostly - // useful for parsing paths like "c:\windows" or "MacVolume:System Folder". - // -#if !PLATFORM_UNIX - public static readonly char VolumeSeparatorChar = ':'; -#else - public static readonly char VolumeSeparatorChar = '/'; -#endif // !PLATFORM_UNIX - - // Platform specific invalid list of characters in a path. - // See the "Naming a File" MSDN conceptual docs for more details on - // what is valid in a file name (which is slightly different from what - // is legal in a path name). - // Note: This list is duplicated in CheckInvalidPathChars - [Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")] -#if !PLATFORM_UNIX - public static readonly char[] InvalidPathChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31 }; -#else - public static readonly char[] InvalidPathChars = { '\0' }; -#endif // !PLATFORM_UNIX - - // Trim trailing white spaces, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space. - // String.WhitespaceChars will trim aggressively than what the underlying FS does (for ex, NTFS, FAT). - internal static readonly char[] TrimEndChars = - { - (char)0x09, // Horizontal tab - (char)0x0A, // Line feed - (char)0x0B, // Vertical tab - (char)0x0C, // Form feed - (char)0x0D, // Carriage return - (char)0x20, // Space - (char)0x85, // Next line - (char)0xA0 // Non breaking space - }; - -#if !PLATFORM_UNIX - private static readonly char[] RealInvalidPathChars = PathInternal.InvalidPathChars; - - private static readonly char[] InvalidFileNameChars = { '\"', '<', '>', '|', '\0', (Char)1, (Char)2, (Char)3, (Char)4, (Char)5, (Char)6, (Char)7, (Char)8, (Char)9, (Char)10, (Char)11, (Char)12, (Char)13, (Char)14, (Char)15, (Char)16, (Char)17, (Char)18, (Char)19, (Char)20, (Char)21, (Char)22, (Char)23, (Char)24, (Char)25, (Char)26, (Char)27, (Char)28, (Char)29, (Char)30, (Char)31, ':', '*', '?', '\\', '/' }; -#else - private static readonly char[] RealInvalidPathChars = { '\0' }; - - private static readonly char[] InvalidFileNameChars = { '\0', '/' }; -#endif // !PLATFORM_UNIX - -#if !PLATFORM_UNIX - public static readonly char PathSeparator = ';'; -#else - public static readonly char PathSeparator = ':'; -#endif // !PLATFORM_UNIX - - - // The max total path is 260, and the max individual component length is 255. - // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars. - internal static readonly int MaxPath = PathInternal.MaxShortPath; - - internal static readonly int MaxPathComponentLength = PathInternal.MaxComponentLength; - - // Windows API definitions - internal const int MAX_PATH = 260; // From WinDef.h - internal const int MAX_DIRECTORY_PATH = 248; // cannot create directories greater than 248 characters - - // Changes the extension of a file path. The path parameter - // specifies a file path, and the extension parameter - // specifies a file extension (with a leading period, such as - // ".exe" or ".cs"). - // - // The function returns a file path with the same root, directory, and base - // name parts as path, but with the file extension changed to - // the specified extension. If path is null, the function - // returns null. If path does not contain a file extension, - // the new file extension is appended to the path. If extension - // is null, any exsiting extension is removed from path. - // - public static String ChangeExtension(String path, String extension) { - if (path != null) { - CheckInvalidPathChars(path); - - String s = path; - for (int i = path.Length; --i >= 0;) { - char ch = path[i]; - if (ch == '.') { - s = path.Substring(0, i); - break; - } - if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) break; - } - if (extension != null && path.Length != 0) { - if (extension.Length == 0 || extension[0] != '.') { - s = s + "."; - } - s = s + extension; - } - return s; - } - return null; - } - - // Returns the directory path of a file path. This method effectively - // removes the last element of the given file path, i.e. it returns a - // string consisting of all characters up to but not including the last - // backslash ("\") in the file path. The returned value is null if the file - // path is null or if the file path denotes a root (such as "\", "C:", or - // "\\server\share"). - public static String GetDirectoryName(String path) - { - return GetDirectoryNameInternal(path); - } - - [System.Security.SecuritySafeCritical] - private static string GetDirectoryNameInternal(string path) - { - if (path != null) - { - CheckInvalidPathChars(path); - - // Expanding short paths is dangerous in this case as the results will change with the current directory. - // - // Suppose you have a path called "PICTUR~1\Foo". Now suppose you have two folders on disk "C:\Mine\Pictures Of Me" - // and "C:\Yours\Pictures of You". If the current directory is neither you'll get back "PICTUR~1". If it is "C:\Mine" - // get back "Pictures Of Me". "C:\Yours" would give back "Pictures of You". - // - // Because of this and as it isn't documented that short paths are expanded we will not expand short names unless - // we're in legacy mode. - string normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths: -#if FEATURE_PATHCOMPAT - AppContextSwitches.UseLegacyPathHandling -#else - false -#endif - ); - - // If there are no permissions for PathDiscovery to this path, we should NOT expand the short paths - // as this would leak information about paths to which the user would not have access to. - if (path.Length > 0 -#if FEATURE_CAS_POLICY - // Only do the extra logic if we're not in full trust - && !CodeAccessSecurityEngine.QuickCheckForAllDemands() -#endif - ) - { - try - { - // If we were passed in a path with \\?\ we need to remove it as FileIOPermission does not like it. - string tempPath = RemoveLongPathPrefix(path); - - // FileIOPermission cannot handle paths that contain ? or * - // So we only pass to FileIOPermission the text up to them. - int pos = 0; - while (pos < tempPath.Length && (tempPath[pos] != '?' && tempPath[pos] != '*')) - pos++; - - // GetFullPath will Demand that we have the PathDiscovery FileIOPermission and thus throw - // SecurityException if we don't. - // While we don't use the result of this call we are using it as a consistent way of - // doing the security checks. - if (pos > 0) - GetFullPath(tempPath.Substring(0, pos)); - } - catch (SecurityException) - { - // If the user did not have permissions to the path, make sure that we don't leak expanded short paths - // Only re-normalize if the original path had a ~ in it. - if (path.IndexOf("~", StringComparison.Ordinal) != -1) - { - normalizedPath = NormalizePath(path, fullCheck: false, expandShortPaths: false); - } - } - catch (PathTooLongException) { } - catch (NotSupportedException) { } // Security can throw this on "c:\foo:" - catch (IOException) { } - catch (ArgumentException) { } // The normalizePath with fullCheck will throw this for file: and http: - } - - path = normalizedPath; - - int root = GetRootLength(path); - int i = path.Length; - if (i > root) - { - i = path.Length; - if (i == root) return null; - while (i > root && path[--i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar); - return path.Substring(0, i); - } - } - return null; - } - - // Gets the length of the root DirectoryInfo or whatever DirectoryInfo markers - // are specified for the first part of the DirectoryInfo name. - // - internal static int GetRootLength(string path) - { - CheckInvalidPathChars(path); - -#if !PLATFORM_UNIX && FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - { - int i = 0; - int length = path.Length; - - if (length >= 1 && (IsDirectorySeparator(path[0]))) - { - // handles UNC names and directories off current drive's root. - i = 1; - if (length >= 2 && (IsDirectorySeparator(path[1]))) - { - i = 2; - int n = 2; - while (i < length && ((path[i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) || --n > 0)) i++; - } - } - else if (length >= 2 && path[1] == VolumeSeparatorChar) - { - // handles A:\foo. - i = 2; - if (length >= 3 && (IsDirectorySeparator(path[2]))) i++; - } - return i; - } - else -#endif // !PLATFORM_UNIX && FEATURE_PATHCOMPAT - { - return PathInternal.GetRootLength(path); - } - } - - internal static bool IsDirectorySeparator(char c) { - return (c==DirectorySeparatorChar || c == AltDirectorySeparatorChar); - } - - public static char[] GetInvalidPathChars() - { - return (char[]) RealInvalidPathChars.Clone(); - } - - public static char[] GetInvalidFileNameChars() - { - return (char[]) InvalidFileNameChars.Clone(); - } - - // Returns the extension of the given path. The returned value includes the - // period (".") character of the extension except when you have a terminal period when you get String.Empty, such as ".exe" or - // ".cpp". The returned value is null if the given path is - // null or if the given path does not include an extension. - // - [Pure] - public static String GetExtension(String path) { - if (path==null) - return null; - - CheckInvalidPathChars(path); - int length = path.Length; - for (int i = length; --i >= 0;) { - char ch = path[i]; - if (ch == '.') - { - if (i != length - 1) - return path.Substring(i, length - i); - else - return String.Empty; - } - if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) - break; - } - return String.Empty; - } - - // Expands the given path to a fully qualified path. The resulting string - // consists of a drive letter, a colon, and a root relative path. This - // function does not verify that the resulting path - // refers to an existing file or directory on the associated volume. - [Pure] - [System.Security.SecuritySafeCritical] - public static String GetFullPath(String path) { - String fullPath = GetFullPathInternal(path); -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, path, fullPath); - state.EnsureState(); -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, fullPath, false, false); -#endif - return fullPath; - } - - [System.Security.SecurityCritical] - internal static String UnsafeGetFullPath(String path) - { - String fullPath = GetFullPathInternal(path); -#if !FEATURE_CORECLR - FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, fullPath, false, false); -#endif - return fullPath; - } - - // This method is package access to let us quickly get a string name - // while avoiding a security check. This also serves a slightly - // different purpose - when we open a file, we need to resolve the - // path into a fully qualified, non-relative path name. This - // method does that, finding the current drive &; directory. But - // as long as we don't return this info to the user, we're good. However, - // the public GetFullPath does need to do a security check. - internal static string GetFullPathInternal(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - Contract.EndContractBlock(); - - string newPath = NormalizePath(path, fullCheck: true); - return newPath; - } - - [System.Security.SecuritySafeCritical] // auto-generated - internal unsafe static string NormalizePath(string path, bool fullCheck) - { - return NormalizePath(path, fullCheck, -#if FEATURE_PATHCOMPAT - AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath : -#endif - PathInternal.MaxLongPath); - } - - [System.Security.SecuritySafeCritical] // auto-generated - internal unsafe static string NormalizePath(string path, bool fullCheck, bool expandShortPaths) - { - return NormalizePath(path, fullCheck, -#if FEATURE_PATHCOMPAT - AppContextSwitches.BlockLongPaths ? PathInternal.MaxShortPath : -#endif - PathInternal.MaxLongPath, - expandShortPaths); - } - - [System.Security.SecuritySafeCritical] // auto-generated - internal static string NormalizePath(string path, bool fullCheck, int maxPathLength) - { - return NormalizePath(path, fullCheck, maxPathLength, expandShortPaths: true); - } - - [System.Security.SecuritySafeCritical] - internal static string NormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - { - return LegacyNormalizePath(path, fullCheck, maxPathLength, expandShortPaths); - } - else -#endif // FEATURE_APPCOMPAT - { - if (PathInternal.IsExtended(path)) - { - // We can't really know what is valid for all cases of extended paths. - // - // - object names can include other characters as well (':', '/', etc.) - // - even file objects have different rules (pipe names can contain most characters) - // - // As such we will do no further analysis of extended paths to avoid blocking known and unknown - // scenarios as well as minimizing compat breaks should we block now and need to unblock later. - return path; - } - - string normalizedPath = null; - - if (fullCheck == false) - { - // Disabled fullCheck is only called by GetDirectoryName and GetPathRoot. - // Avoid adding addtional callers and try going direct to lighter weight NormalizeDirectorySeparators. - normalizedPath = NewNormalizePathLimitedChecks(path, maxPathLength, expandShortPaths); - } - else - { - normalizedPath = NewNormalizePath(path, maxPathLength, expandShortPaths: true); - } - - if (string.IsNullOrWhiteSpace(normalizedPath)) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - return normalizedPath; - } - } - - [System.Security.SecuritySafeCritical] - private static string NewNormalizePathLimitedChecks(string path, int maxPathLength, bool expandShortPaths) - { - string normalized = PathInternal.NormalizeDirectorySeparators(path); - - if (PathInternal.IsPathTooLong(normalized) || PathInternal.AreSegmentsTooLong(normalized)) - throw new PathTooLongException(); - -#if !PLATFORM_UNIX - if (!PathInternal.IsDevice(normalized) && PathInternal.HasInvalidVolumeSeparator(path)) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - if (expandShortPaths && normalized.IndexOf('~') != -1) - { - try - { - return LongPathHelper.GetLongPathName(normalized); - } - catch - { - // Don't care if we can't get the long path- might not exist, etc. - } - } -#endif - - return normalized; - } - - /// <summary> - /// Normalize the path and check for bad characters or other invalid syntax. - /// </summary> - [System.Security.SecuritySafeCritical] - [ResourceExposure(ResourceScope.Machine)] - [ResourceConsumption(ResourceScope.Machine)] - private static string NewNormalizePath(string path, int maxPathLength, bool expandShortPaths) - { - Contract.Requires(path != null, "path can't be null"); - - // Embedded null characters are the only invalid character case we want to check up front. - // This is because the nulls will signal the end of the string to Win32 and therefore have - // unpredictable results. Other invalid characters we give a chance to be normalized out. - if (path.IndexOf('\0') != -1) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); - -#if !PLATFORM_UNIX - // Note that colon and wildcard checks happen in FileIOPermissions - - // Technically this doesn't matter but we used to throw for this case - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - // We don't want to check invalid characters for device format- see comments for extended above - return LongPathHelper.Normalize(path, (uint)maxPathLength, checkInvalidCharacters: !PathInternal.IsDevice(path), expandShortPaths: expandShortPaths); -#else - if (path.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - // Expand with current directory if necessary - if (!IsPathRooted(path)) - path = Combine(Directory.GetCurrentDirectory(), path); - - // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist, - // and turns it into a full path, which we only want if fullCheck is true. - string collapsedString = PathInternal.RemoveRelativeSegments(path); - - if (collapsedString.Length > maxPathLength) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - return collapsedString.Length == 0 ? "/" : collapsedString; -#endif // PLATFORM_UNIX - } - -#if FEATURE_PATHCOMPAT - [System.Security.SecurityCritical] // auto-generated - internal unsafe static String LegacyNormalizePath(String path, bool fullCheck, int maxPathLength, bool expandShortPaths) { - - Contract.Requires(path != null, "path can't be null"); - // If we're doing a full path check, trim whitespace and look for - // illegal path characters. - if (fullCheck) { - // Trim whitespace off the end of the string. - // Win32 normalization trims only U+0020. - path = path.TrimEnd(TrimEndChars); - - // Look for illegal path characters. - if (PathInternal.AnyPathHasIllegalCharacters(path)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); - } - - int index = 0; - // We prefer to allocate on the stack for workingset/perf gain. If the - // starting path is less than MaxPath then we can stackalloc; otherwise we'll - // use a StringBuilder (PathHelper does this under the hood). The latter may - // happen in 2 cases: - // 1. Starting path is greater than MaxPath but it normalizes down to MaxPath. - // This is relevant for paths containing escape sequences. In this case, we - // attempt to normalize down to MaxPath, but the caller pays a perf penalty - // since StringBuilder is used. - // 2. IsolatedStorage, which supports paths longer than MaxPath (value given - // by maxPathLength. - PathHelper newBuffer; - if (path.Length + 1 <= MaxPath) { - char* m_arrayPtr = stackalloc char[MaxPath]; - newBuffer = new PathHelper(m_arrayPtr, MaxPath); - } else { - newBuffer = new PathHelper(path.Length + Path.MaxPath, maxPathLength); - } - - uint numSpaces = 0; - uint numDots = 0; - bool fixupDirectorySeparator = false; - // Number of significant chars other than potentially suppressible - // dots and spaces since the last directory or volume separator char - uint numSigChars = 0; - int lastSigChar = -1; // Index of last significant character. - // Whether this segment of the path (not the complete path) started - // with a volume separator char. Reject "c:...". - bool startedWithVolumeSeparator = false; - bool firstSegment = true; - int lastDirectorySeparatorPos = 0; - -#if !PLATFORM_UNIX - bool mightBeShortFileName = false; - - // LEGACY: This code is here for backwards compatibility reasons. It - // ensures that \\foo.cs\bar.cs stays \\foo.cs\bar.cs instead of being - // turned into \foo.cs\bar.cs. - if (path.Length > 0 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) { - newBuffer.Append('\\'); - index++; - lastSigChar = 0; - } -#endif - - // Normalize the string, stripping out redundant dots, spaces, and - // slashes. - while (index < path.Length) { - char currentChar = path[index]; - - // We handle both directory separators and dots specially. For - // directory separators, we consume consecutive appearances. - // For dots, we consume all dots beyond the second in - // succession. All other characters are added as is. In - // addition we consume all spaces after the last other char - // in a directory name up until the directory separator. - - if (currentChar == DirectorySeparatorChar || currentChar == AltDirectorySeparatorChar) { - // If we have a path like "123.../foo", remove the trailing dots. - // However, if we found "c:\temp\..\bar" or "c:\temp\...\bar", don't. - // Also remove trailing spaces from both files & directory names. - // This was agreed on with the OS team to fix undeletable directory - // names ending in spaces. - - // If we saw a '\' as the previous last significant character and - // are simply going to write out dots, suppress them. - // If we only contain dots and slashes though, only allow - // a string like [dot]+ [space]*. Ignore everything else. - // Legal: "\.. \", "\...\", "\. \" - // Illegal: "\.. .\", "\. .\", "\ .\" - if (numSigChars == 0) { - // Dot and space handling - if (numDots > 0) { - // Look for ".[space]*" or "..[space]*" - int start = lastSigChar + 1; - if (path[start] != '.') - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - // Only allow "[dot]+[space]*", and normalize the - // legal ones to "." or ".." - if (numDots >= 2) { - // Reject "C:..." - if (startedWithVolumeSeparator && numDots > 2) - - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - if (path[start + 1] == '.') { - // Search for a space in the middle of the - // dots and throw - for(int i=start + 2; i < start + numDots; i++) { - if (path[i] != '.') - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - } - - numDots = 2; - } - else { - if (numDots > 1) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - numDots = 1; - } - } - - if (numDots == 2) { - newBuffer.Append('.'); - } - - newBuffer.Append('.'); - fixupDirectorySeparator = false; - - // Continue in this case, potentially writing out '\'. - } - - if (numSpaces > 0 && firstSegment) { - // Handle strings like " \\server\share". - if (index + 1 < path.Length && - (path[index + 1] == DirectorySeparatorChar || path[index + 1] == AltDirectorySeparatorChar)) - { - newBuffer.Append(DirectorySeparatorChar); - } - } - } - numDots = 0; - numSpaces = 0; // Suppress trailing spaces - - if (!fixupDirectorySeparator) { - fixupDirectorySeparator = true; - newBuffer.Append(DirectorySeparatorChar); - } - numSigChars = 0; - lastSigChar = index; - startedWithVolumeSeparator = false; - firstSegment = false; - -#if !PLATFORM_UNIX - // For short file names, we must try to expand each of them as - // soon as possible. We need to allow people to specify a file - // name that doesn't exist using a path with short file names - // in it, such as this for a temp file we're trying to create: - // C:\DOCUME~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp - // We could try doing this afterwards piece by piece, but it's - // probably a lot simpler to do it here. - if (mightBeShortFileName) { - newBuffer.TryExpandShortFileName(); - mightBeShortFileName = false; - } -#endif - int thisPos = newBuffer.Length - 1; - if (thisPos - lastDirectorySeparatorPos > MaxPathComponentLength) - { - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - } - lastDirectorySeparatorPos = thisPos; - } // if (Found directory separator) - else if (currentChar == '.') { - // Reduce only multiple .'s only after slash to 2 dots. For - // instance a...b is a valid file name. - numDots++; - // Don't flush out non-terminal spaces here, because they may in - // the end not be significant. Turn "c:\ . .\foo" -> "c:\foo" - // which is the conclusion of removing trailing dots & spaces, - // as well as folding multiple '\' characters. - } - else if (currentChar == ' ') { - numSpaces++; - } - else { // Normal character logic -#if !PLATFORM_UNIX - if (currentChar == '~' && expandShortPaths) - mightBeShortFileName = true; -#endif - - fixupDirectorySeparator = false; - -#if !PLATFORM_UNIX - // To reject strings like "C:...\foo" and "C :\foo" - if (firstSegment && currentChar == VolumeSeparatorChar) { - // Only accept "C:", not "c :" or ":" - // Get a drive letter or ' ' if index is 0. - char driveLetter = (index > 0) ? path[index-1] : ' '; - bool validPath = ((numDots == 0) && (numSigChars >= 1) && (driveLetter != ' ')); - if (!validPath) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - startedWithVolumeSeparator = true; - // We need special logic to make " c:" work, we should not fix paths like " foo::$DATA" - if (numSigChars > 1) { // Common case, simply do nothing - int spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset. - while((spaceCount < newBuffer.Length) && newBuffer[spaceCount] == ' ') - spaceCount++; - if (numSigChars - spaceCount == 1) { - //Safe to update stack ptr directly - newBuffer.Length = 0; - newBuffer.Append(driveLetter); // Overwrite spaces, we need a special case to not break " foo" as a relative path. - } - } - numSigChars = 0; - } - else -#endif // !PLATFORM_UNIX - { - numSigChars += 1 + numDots + numSpaces; - } - - // Copy any spaces & dots since the last significant character - // to here. Note we only counted the number of dots & spaces, - // and don't know what order they're in. Hence the copy. - if (numDots > 0 || numSpaces > 0) { - int numCharsToCopy = (lastSigChar >= 0) ? index - lastSigChar - 1 : index; - if (numCharsToCopy > 0) { - for (int i=0; i<numCharsToCopy; i++) { - newBuffer.Append(path[lastSigChar + 1 + i]); - } - } - numDots = 0; - numSpaces = 0; - } - - newBuffer.Append(currentChar); - lastSigChar = index; - } - - index++; - } // end while - - if (newBuffer.Length - 1 - lastDirectorySeparatorPos > MaxPathComponentLength) - { - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - } - - // Drop any trailing dots and spaces from file & directory names, EXCEPT - // we MUST make sure that "C:\foo\.." is correctly handled. - // Also handle "C:\foo\." -> "C:\foo", while "C:\." -> "C:\" - if (numSigChars == 0) { - if (numDots > 0) { - // Look for ".[space]*" or "..[space]*" - int start = lastSigChar + 1; - if (path[start] != '.') - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - // Only allow "[dot]+[space]*", and normalize the - // legal ones to "." or ".." - if (numDots >= 2) { - // Reject "C:..." - if (startedWithVolumeSeparator && numDots > 2) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - if (path[start + 1] == '.') { - // Search for a space in the middle of the - // dots and throw - for(int i=start + 2; i < start + numDots; i++) { - if (path[i] != '.') - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - } - - numDots = 2; - } - else { - if (numDots > 1) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - numDots = 1; - } - } - - if (numDots == 2) { - newBuffer.Append('.'); - } - - newBuffer.Append('.'); - } - } // if (numSigChars == 0) - - // If we ended up eating all the characters, bail out. - if (newBuffer.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegal")); - - // Disallow URL's here. Some of our other Win32 API calls will reject - // them later, so we might be better off rejecting them here. - // Note we've probably turned them into "file:\D:\foo.tmp" by now. - // But for compatibility, ensure that callers that aren't doing a - // full check aren't rejected here. - if (fullCheck) { - if ( newBuffer.OrdinalStartsWith("http:", false) || - newBuffer.OrdinalStartsWith("file:", false)) - { - throw new ArgumentException(Environment.GetResourceString("Argument_PathUriFormatNotSupported")); - } - } - -#if !PLATFORM_UNIX - // If the last part of the path (file or directory name) had a tilde, - // expand that too. - if (mightBeShortFileName) { - newBuffer.TryExpandShortFileName(); - } -#endif - - // Call the Win32 API to do the final canonicalization step. - int result = 1; - - if (fullCheck) { - // NOTE: Win32 GetFullPathName requires the input buffer to be big enough to fit the initial - // path which is a concat of CWD and the relative path, this can be of an arbitrary - // size and could be > MAX_PATH (which becomes an artificial limit at this point), - // even though the final normalized path after fixing up the relative path syntax - // might be well within the MAX_PATH restriction. For ex, - // "c:\SomeReallyLongDirName(thinkGreaterThan_MAXPATH)\..\foo.txt" which actually requires a - // buffer well with in the MAX_PATH as the normalized path is just "c:\foo.txt" - // This buffer requirement seems wrong, it could be a bug or a perf optimization - // like returning required buffer length quickly or avoid stratch buffer etc. - // Ideally we would get the required buffer length first by calling GetFullPathName - // once without the buffer and use that in the later call but this doesn't always work - // due to Win32 GetFullPathName bug. For instance, in Win2k, when the path we are trying to - // fully qualify is a single letter name (such as "a", "1", ",") GetFullPathName - // fails to return the right buffer size (i.e, resulting in insufficient buffer). - // To workaround this bug we will start with MAX_PATH buffer and grow it once if the - // return value is > MAX_PATH. - - result = newBuffer.GetFullPathName(); - -#if !PLATFORM_UNIX - // If we called GetFullPathName with something like "foo" and our - // command window was in short file name mode (ie, by running edlin or - // DOS versions of grep, etc), we might have gotten back a short file - // name. So, check to see if we need to expand it. - mightBeShortFileName = false; - for(int i=0; i < newBuffer.Length && !mightBeShortFileName; i++) { - if (newBuffer[i] == '~' && expandShortPaths) - mightBeShortFileName = true; - } - - if (mightBeShortFileName) { - bool r = newBuffer.TryExpandShortFileName(); - // Consider how the path "Doesn'tExist" would expand. If - // we add in the current directory, it too will need to be - // fully expanded, which doesn't happen if we use a file - // name that doesn't exist. - if (!r) { - int lastSlash = -1; - - for (int i = newBuffer.Length - 1; i >= 0; i--) { - if (newBuffer[i] == DirectorySeparatorChar) { - lastSlash = i; - break; - } - } - - if (lastSlash >= 0) { - - // This bounds check is for safe memcpy but we should never get this far - if (newBuffer.Length >= maxPathLength) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - int lenSavedName = newBuffer.Length - lastSlash - 1; - Contract.Assert(lastSlash < newBuffer.Length, "path unexpectedly ended in a '\'"); - - newBuffer.Fixup(lenSavedName, lastSlash); - } - } - } -#endif // PLATFORM_UNIX - } - - if (result != 0) { - /* Throw an ArgumentException for paths like \\, \\server, \\server\ - This check can only be properly done after normalizing, so - \\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\ - (an internal kernel path) because it provides aliases for drives. */ - if (newBuffer.Length > 1 && newBuffer[0] == '\\' && newBuffer[1] == '\\') { - int startIndex = 2; - while (startIndex < result) { - if (newBuffer[startIndex] == '\\') { - startIndex++; - break; - } - else { - startIndex++; - } - } - if (startIndex == result) - throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC")); - - // Check for \\?\Globalroot, an internal mechanism to the kernel - // that provides aliases for drives and other undocumented stuff. - // The kernel team won't even describe the full set of what - // is available here - we don't want managed apps mucking - // with this for security reasons. - if ( newBuffer.OrdinalStartsWith("\\\\?\\globalroot", true)) - throw new ArgumentException(Environment.GetResourceString("Arg_PathGlobalRoot")); - } - } - - // Check our result and form the managed string as necessary. - if (newBuffer.Length >= maxPathLength) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - if (result == 0) { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == 0) - errorCode = Win32Native.ERROR_BAD_PATHNAME; - __Error.WinIOError(errorCode, path); - return null; // Unreachable - silence a compiler error. - } - - return newBuffer.ToStringOrExisting(path); - } -#endif // FEATURE_PATHCOMPAT - - internal const int MaxLongPath = PathInternal.MaxLongPath; - - private const string LongPathPrefix = PathInternal.ExtendedPathPrefix; - private const string UNCPathPrefix = PathInternal.UncPathPrefix; - private const string UNCLongPathPrefixToInsert = PathInternal.UncExtendedPrefixToInsert; - private const string UNCLongPathPrefix = PathInternal.UncExtendedPathPrefix; - - internal static bool HasLongPathPrefix(string path) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - return path.StartsWith(LongPathPrefix, StringComparison.Ordinal); - else -#endif - return PathInternal.IsExtended(path); - } - - internal static string AddLongPathPrefix(string path) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - { - if (path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) - return path; - - if (path.StartsWith(UNCPathPrefix, StringComparison.Ordinal)) - return path.Insert(2, UNCLongPathPrefixToInsert); // Given \\server\share in longpath becomes \\?\UNC\server\share => UNCLongPathPrefix + path.SubString(2); => The actual command simply reduces the operation cost. - - return LongPathPrefix + path; - } - else -#endif - { - return PathInternal.EnsureExtendedPrefix(path); - } - } - - internal static string RemoveLongPathPrefix(string path) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - { - if (!path.StartsWith(LongPathPrefix, StringComparison.Ordinal)) - return path; - - if (path.StartsWith(UNCLongPathPrefix, StringComparison.OrdinalIgnoreCase)) - return path.Remove(2, 6); // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. - - return path.Substring(4); - } - else -#endif - { - return PathInternal.RemoveExtendedPrefix(path); - } - } - - internal static StringBuilder RemoveLongPathPrefix(StringBuilder pathSB) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.UseLegacyPathHandling) - { - if (!PathInternal.StartsWithOrdinal(pathSB, LongPathPrefix)) - return pathSB; - - // Given \\?\UNC\server\share we return \\server\share => @'\\' + path.SubString(UNCLongPathPrefix.Length) => The actual command simply reduces the operation cost. - if (PathInternal.StartsWithOrdinal(pathSB, UNCLongPathPrefix, ignoreCase: true)) - return pathSB.Remove(2, 6); - - return pathSB.Remove(0, 4); - } - else -#endif - { - return PathInternal.RemoveExtendedPrefix(pathSB); - } - } - - - // Returns the name and extension parts of the given path. The resulting - // string contains the characters of path that follow the last - // backslash ("\"), slash ("/"), or colon (":") character in - // path. The resulting string is the entire path if path - // contains no backslash after removing trailing slashes, slash, or colon characters. The resulting - // string is null if path is null. - // - [Pure] - public static String GetFileName(String path) { - if (path != null) { - CheckInvalidPathChars(path); - - int length = path.Length; - for (int i = length; --i >= 0;) { - char ch = path[i]; - if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) - return path.Substring(i + 1, length - i - 1); - - } - } - return path; - } - - [Pure] - public static String GetFileNameWithoutExtension(String path) { - path = GetFileName(path); - if (path != null) - { - int i; - if ((i=path.LastIndexOf('.')) == -1) - return path; // No path extension found - else - return path.Substring(0,i); - } - return null; - } - - - - // Returns the root portion of the given path. The resulting string - // consists of those rightmost characters of the path that constitute the - // root of the path. Possible patterns for the resulting string are: An - // empty string (a relative path on the current drive), "\" (an absolute - // path on the current drive), "X:" (a relative path on a given drive, - // where X is the drive letter), "X:\" (an absolute path on a given drive), - // and "\\server\share" (a UNC path for a given server and share name). - // The resulting string is null if path is null. - // - [Pure] - public static String GetPathRoot(String path) { - if (path == null) return null; - - // Expanding short paths has no impact on the path root- there is no such thing as an - // 8.3 volume or server/share name. - path = NormalizePath(path, fullCheck: false, expandShortPaths: false); - return path.Substring(0, GetRootLength(path)); - } - - [System.Security.SecuritySafeCritical] - public static String GetTempPath() - { -#if !FEATURE_CORECLR - new EnvironmentPermission(PermissionState.Unrestricted).Demand(); -#endif - StringBuilder sb = new StringBuilder(PathInternal.MaxShortPath); - uint r = Win32Native.GetTempPath(PathInternal.MaxShortPath, sb); - String path = sb.ToString(); - if (r==0) __Error.WinIOError(); - path = GetFullPathInternal(path); -#if FEATURE_CORECLR - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, path); - state.EnsureState(); -#endif - return path; - } - - internal static bool IsRelative(string path) - { - Contract.Assert(path != null, "path can't be null"); - return PathInternal.IsPartiallyQualified(path); - } - - // Returns a cryptographically strong random 8.3 string that can be - // used as either a folder name or a file name. -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] -#endif - public static String GetRandomFileName() - { - // 5 bytes == 40 bits == 40/5 == 8 chars in our encoding - // This gives us exactly 8 chars. We want to avoid the 8.3 short name issue - byte[] key = new byte[10]; - -#if FEATURE_CORECLR - Win32Native.Random(true, key, key.Length); -#else - // RNGCryptoServiceProvider is disposable in post-Orcas desktop mscorlibs, but not in CoreCLR's - // mscorlib, so we need to do a manual using block for it. - RNGCryptoServiceProvider rng = null; - try - { - rng = new RNGCryptoServiceProvider(); - - rng.GetBytes(key); - } - finally - { - if (rng != null) - { - rng.Dispose(); - } - } -#endif - - // rndCharArray is expected to be 16 chars - char[] rndCharArray = Path.ToBase32StringSuitableForDirName(key).ToCharArray(); - rndCharArray[8] = '.'; - return new String(rndCharArray, 0, 12); - } - - // Returns a unique temporary file name, and creates a 0-byte file by that - // name on disk. - [System.Security.SecuritySafeCritical] - public static String GetTempFileName() - { - return InternalGetTempFileName(true); - } - - [System.Security.SecurityCritical] - internal static String UnsafeGetTempFileName() - { - return InternalGetTempFileName(false); - } - - [System.Security.SecurityCritical] - private static String InternalGetTempFileName(bool checkHost) - { - String path = GetTempPath(); - - // Since this can write to the temp directory and theoretically - // cause a denial of service attack, demand FileIOPermission to - // that directory. - -#if FEATURE_CORECLR - if (checkHost) - { - FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, String.Empty, path); - state.EnsureState(); - } -#else - FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, path); -#endif - StringBuilder sb = new StringBuilder(MaxPath); - uint r = Win32Native.GetTempFileName(path, "tmp", 0, sb); - if (r==0) __Error.WinIOError(); - return sb.ToString(); - } - - // Tests if a path includes a file extension. The result is - // true if the characters that follow the last directory - // separator ('\\' or '/') or volume separator (':') in the path include - // a period (".") other than a terminal period. The result is false otherwise. - // - [Pure] - public static bool HasExtension(String path) { - if (path != null) { - CheckInvalidPathChars(path); - - for (int i = path.Length; --i >= 0;) { - char ch = path[i]; - if (ch == '.') { - if ( i != path.Length - 1) - return true; - else - return false; - } - if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar) break; - } - } - return false; - } - - // Tests if the given path contains a root. A path is considered rooted - // if it starts with a backslash ("\") or a drive letter and a colon (":"). - // - [Pure] - public static bool IsPathRooted(String path) { - if (path != null) { - CheckInvalidPathChars(path); - - int length = path.Length; -#if !PLATFORM_UNIX - if ((length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) || (length >= 2 && path[1] == VolumeSeparatorChar)) - return true; -#else - if (length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) - return true; -#endif - } - return false; - } - - public static String Combine(String path1, String path2) { - if (path1==null || path2==null) - throw new ArgumentNullException((path1==null) ? "path1" : "path2"); - Contract.EndContractBlock(); - CheckInvalidPathChars(path1); - CheckInvalidPathChars(path2); - - return CombineNoChecks(path1, path2); - } - - public static String Combine(String path1, String path2, String path3) { - if (path1 == null || path2 == null || path3 == null) - throw new ArgumentNullException((path1 == null) ? "path1" : (path2 == null) ? "path2" : "path3"); - Contract.EndContractBlock(); - CheckInvalidPathChars(path1); - CheckInvalidPathChars(path2); - CheckInvalidPathChars(path3); - - return CombineNoChecks(CombineNoChecks(path1, path2), path3); - } - - public static String Combine(String path1, String path2, String path3, String path4) { - if (path1 == null || path2 == null || path3 == null || path4 == null) - throw new ArgumentNullException((path1 == null) ? "path1" : (path2 == null) ? "path2" : (path3 == null) ? "path3" : "path4"); - Contract.EndContractBlock(); - CheckInvalidPathChars(path1); - CheckInvalidPathChars(path2); - CheckInvalidPathChars(path3); - CheckInvalidPathChars(path4); - - return CombineNoChecks(CombineNoChecks(CombineNoChecks(path1, path2), path3), path4); - } - - public static String Combine(params String[] paths) { - if (paths == null) { - throw new ArgumentNullException("paths"); - } - Contract.EndContractBlock(); - - int finalSize = 0; - int firstComponent = 0; - - // We have two passes, the first calcuates how large a buffer to allocate and does some precondition - // checks on the paths passed in. The second actually does the combination. - - for (int i = 0; i < paths.Length; i++) { - if (paths[i] == null) { - throw new ArgumentNullException("paths"); - } - - if (paths[i].Length == 0) { - continue; - } - - CheckInvalidPathChars(paths[i]); - - if (Path.IsPathRooted(paths[i])) { - firstComponent = i; - finalSize = paths[i].Length; - } else { - finalSize += paths[i].Length; - } - - char ch = paths[i][paths[i].Length - 1]; - if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar) - finalSize++; - } - - StringBuilder finalPath = StringBuilderCache.Acquire(finalSize); - - for (int i = firstComponent; i < paths.Length; i++) { - if (paths[i].Length == 0) { - continue; - } - - if (finalPath.Length == 0) { - finalPath.Append(paths[i]); - } else { - char ch = finalPath[finalPath.Length - 1]; - if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar) { - finalPath.Append(DirectorySeparatorChar); - } - - finalPath.Append(paths[i]); - } - } - - return StringBuilderCache.GetStringAndRelease(finalPath); - } - - private static String CombineNoChecks(String path1, String path2) { - if (path2.Length == 0) - return path1; - - if (path1.Length == 0) - return path2; - - if (IsPathRooted(path2)) - return path2; - - char ch = path1[path1.Length - 1]; - if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar) - return path1 + DirectorySeparatorCharAsString + path2; - return path1 + path2; - } - - private static readonly Char[] s_Base32Char = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z', '0', '1', '2', '3', '4', '5'}; - - internal static String ToBase32StringSuitableForDirName(byte[] buff) - { - // This routine is optimised to be used with buffs of length 20 - Contract.Assert(((buff.Length % 5) == 0), "Unexpected hash length"); - - StringBuilder sb = StringBuilderCache.Acquire(); - byte b0, b1, b2, b3, b4; - int l, i; - - l = buff.Length; - i = 0; - - // Create l chars using the last 5 bits of each byte. - // Consume 3 MSB bits 5 bytes at a time. - - do - { - b0 = (i < l) ? buff[i++] : (byte)0; - b1 = (i < l) ? buff[i++] : (byte)0; - b2 = (i < l) ? buff[i++] : (byte)0; - b3 = (i < l) ? buff[i++] : (byte)0; - b4 = (i < l) ? buff[i++] : (byte)0; - - // Consume the 5 Least significant bits of each byte - sb.Append(s_Base32Char[b0 & 0x1F]); - sb.Append(s_Base32Char[b1 & 0x1F]); - sb.Append(s_Base32Char[b2 & 0x1F]); - sb.Append(s_Base32Char[b3 & 0x1F]); - sb.Append(s_Base32Char[b4 & 0x1F]); - - // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4 - sb.Append(s_Base32Char[( - ((b0 & 0xE0) >> 5) | - ((b3 & 0x60) >> 2))]); - - sb.Append(s_Base32Char[( - ((b1 & 0xE0) >> 5) | - ((b4 & 0x60) >> 2))]); - - // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4 - - b2 >>= 5; - - Contract.Assert(((b2 & 0xF8) == 0), "Unexpected set bits"); - - if ((b3 & 0x80) != 0) - b2 |= 0x08; - if ((b4 & 0x80) != 0) - b2 |= 0x10; - - sb.Append(s_Base32Char[b2]); - - } while (i < l); - - return StringBuilderCache.GetStringAndRelease(sb); - } - - // ".." can only be used if it is specified as a part of a valid File/Directory name. We disallow - // the user being able to use it to move up directories. Here are some examples eg - // Valid: a..b abc..d - // Invalid: ..ab ab.. .. abc..d\abc.. - // - internal static void CheckSearchPattern(String searchPattern) - { - int index; - while ((index = searchPattern.IndexOf("..", StringComparison.Ordinal)) != -1) { - - if (index + 2 == searchPattern.Length) // Terminal ".." . Files names cannot end in ".." - throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSearchPattern")); - - if ((searchPattern[index+2] == DirectorySeparatorChar) - || (searchPattern[index+2] == AltDirectorySeparatorChar)) - throw new ArgumentException(Environment.GetResourceString("Arg_InvalidSearchPattern")); - - searchPattern = searchPattern.Substring(index + 2); - } - - } - - internal static void CheckInvalidPathChars(String path, bool checkAdditional = false) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (PathInternal.HasIllegalCharacters(path, checkAdditional)) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidPathChars")); - } - - internal static String InternalCombine(String path1, String path2) { - if (path1==null || path2==null) - throw new ArgumentNullException((path1==null) ? "path1" : "path2"); - Contract.EndContractBlock(); - CheckInvalidPathChars(path1); - CheckInvalidPathChars(path2); - - if (path2.Length == 0) - throw new ArgumentException(Environment.GetResourceString("Argument_PathEmpty"), "path2"); - if (IsPathRooted(path2)) - throw new ArgumentException(Environment.GetResourceString("Arg_Path2IsRooted"), "path2"); - int i = path1.Length; - if (i == 0) return path2; - char ch = path1[i - 1]; - if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && ch != VolumeSeparatorChar) - return path1 + DirectorySeparatorCharAsString + path2; - return path1 + path2; - } - - } -} diff --git a/src/mscorlib/src/System/IO/PathHelper.cs b/src/mscorlib/src/System/IO/PathHelper.cs deleted file mode 100644 index 8e39b3c537..0000000000 --- a/src/mscorlib/src/System/IO/PathHelper.cs +++ /dev/null @@ -1,448 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if FEATURE_PATHCOMPAT -using System; -using System.Collections; -using System.Text; -using Microsoft.Win32; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Globalization; -using System.Runtime.Versioning; -using System.Security; -using System.Security.Permissions; -using System.Diagnostics.Contracts; - -namespace System.IO { - - // ABOUT: - // Helps with path normalization; support allocating on the stack or heap - // - // PathHelper can't stackalloc the array for obvious reasons; you must pass - // in an array of chars allocated on the stack. - // - // USAGE: - // Suppose you need to represent a char array of length len. Then this is the - // suggested way to instantiate PathHelper: - // *************************************************************************** - // PathHelper pathHelper; - // if (charArrayLength less than stack alloc threshold == Path.MaxPath) - // char* arrayPtr = stackalloc char[Path.MaxPath]; - // pathHelper = new PathHelper(arrayPtr); - // else - // pathHelper = new PathHelper(capacity, maxPath); - // *************************************************************************** - // - // note in the StringBuilder ctor: - // - maxPath may be greater than Path.MaxPath (for isolated storage) - // - capacity may be greater than maxPath. This is even used for non-isolated - // storage scenarios where we want to temporarily allow strings greater - // than Path.MaxPath if they can be normalized down to Path.MaxPath. This - // can happen if the path contains escape characters "..". - // - unsafe internal struct PathHelper { // should not be serialized - - // maximum size, max be greater than max path if contains escape sequence - private int m_capacity; - // current length (next character position) - private int m_length; - // max path, may be less than capacity - private int m_maxPath; - - // ptr to stack alloc'd array of chars - [SecurityCritical] - private char* m_arrayPtr; - - // StringBuilder - private StringBuilder m_sb; - - // whether to operate on stack alloc'd or heap alloc'd array - private bool useStackAlloc; - - // Whether to skip calls to Win32Native.GetLongPathName becasue we tried before and failed: - private bool doNotTryExpandShortFileName; - - // Instantiates a PathHelper with a stack alloc'd array of chars - [System.Security.SecurityCritical] - internal PathHelper(char* charArrayPtr, int length) { - Contract.Requires(charArrayPtr != null); - // force callers to be aware of this - Contract.Requires(length == Path.MaxPath); - this.m_length = 0; - this.m_sb = null; - - this.m_arrayPtr = charArrayPtr; - this.m_capacity = length; - this.m_maxPath = Path.MaxPath; - useStackAlloc = true; - doNotTryExpandShortFileName = false; - } - - // Instantiates a PathHelper with a heap alloc'd array of ints. Will create a StringBuilder - [System.Security.SecurityCritical] - internal PathHelper(int capacity, int maxPath) - { - this.m_length = 0; - this.m_arrayPtr = null; - this.useStackAlloc = false; - - this.m_sb = new StringBuilder(capacity); - this.m_capacity = capacity; - this.m_maxPath = maxPath; - doNotTryExpandShortFileName = false; - } - - internal int Length { - get { - if (useStackAlloc) { - return m_length; - } - else { - return m_sb.Length; - } - } - set { - if (useStackAlloc) { - m_length = value; - } - else { - m_sb.Length = value; - } - } - } - - internal int Capacity { - get { - return m_capacity; - } - } - - internal char this[int index] { - [System.Security.SecurityCritical] - get { - Contract.Requires(index >= 0 && index < Length); - if (useStackAlloc) { - return m_arrayPtr[index]; - } - else { - return m_sb[index]; - } - } - [System.Security.SecurityCritical] - set { - Contract.Requires(index >= 0 && index < Length); - if (useStackAlloc) { - m_arrayPtr[index] = value; - } - else { - m_sb[index] = value; - } - } - } - - [System.Security.SecurityCritical] - internal unsafe void Append(char value) { - if (Length + 1 >= m_capacity) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - if (useStackAlloc) { - m_arrayPtr[Length] = value; - m_length++; - } - else { - m_sb.Append(value); - } - } - - [System.Security.SecurityCritical] - internal unsafe int GetFullPathName() { - if (useStackAlloc) { - char* finalBuffer = stackalloc char[Path.MaxPath + 1]; - int result = Win32Native.GetFullPathName(m_arrayPtr, Path.MaxPath + 1, finalBuffer, IntPtr.Zero); - - // If success, the return buffer length does not account for the terminating null character. - // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. - // If failure, the return buffer length is zero - if (result > Path.MaxPath) { - char* tempBuffer = stackalloc char[result]; - finalBuffer = tempBuffer; - result = Win32Native.GetFullPathName(m_arrayPtr, result, finalBuffer, IntPtr.Zero); - } - - // Full path is genuinely long - if (result >= Path.MaxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - Contract.Assert(result < Path.MaxPath, "did we accidently remove a PathTooLongException check?"); - if (result == 0 && m_arrayPtr[0] != '\0') { - __Error.WinIOError(); - } - - else if (result < Path.MaxPath) { - // Null terminate explicitly (may be only needed for some cases such as empty strings) - // GetFullPathName return length doesn't account for null terminating char... - finalBuffer[result] = '\0'; // Safe to write directly as result is < Path.MaxPath - } - - // We have expanded the paths and GetLongPathName may or may not behave differently from before. - // We need to call it again to see: - doNotTryExpandShortFileName = false; - - String.wstrcpy(m_arrayPtr, finalBuffer, result); - // Doesn't account for null terminating char. Think of this as the last - // valid index into the buffer but not the length of the buffer - Length = result; - return result; - } - else { - StringBuilder finalBuffer = new StringBuilder(m_capacity + 1); - int result = Win32Native.GetFullPathName(m_sb.ToString(), m_capacity + 1, finalBuffer, IntPtr.Zero); - - // If success, the return buffer length does not account for the terminating null character. - // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. - // If failure, the return buffer length is zero - if (result > m_maxPath) { - finalBuffer.Length = result; - result = Win32Native.GetFullPathName(m_sb.ToString(), result, finalBuffer, IntPtr.Zero); - } - - // Fullpath is genuinely long - if (result >= m_maxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - Contract.Assert(result < m_maxPath, "did we accidentally remove a PathTooLongException check?"); - if (result == 0 && m_sb[0] != '\0') { - if (Length >= m_maxPath) { - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - } - __Error.WinIOError(); - } - - // We have expanded the paths and GetLongPathName may or may not behave differently from before. - // We need to call it again to see: - doNotTryExpandShortFileName = false; - - m_sb = finalBuffer; - return result; - } - } - - [System.Security.SecurityCritical] - internal unsafe bool TryExpandShortFileName() { - - if (doNotTryExpandShortFileName) - return false; - - if (useStackAlloc) { - NullTerminate(); - char* buffer = UnsafeGetArrayPtr(); - char* shortFileNameBuffer = stackalloc char[Path.MaxPath + 1]; - - int r = Win32Native.GetLongPathName(buffer, shortFileNameBuffer, Path.MaxPath); - - // If success, the return buffer length does not account for the terminating null character. - // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. - // If failure, the return buffer length is zero - if (r >= Path.MaxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - if (r == 0) { - // Note: GetLongPathName will return ERROR_INVALID_FUNCTION on a - // path like \\.\PHYSICALDEVICE0 - some device driver doesn't - // support GetLongPathName on that string. This behavior is - // by design, according to the Core File Services team. - // We also get ERROR_NOT_ENOUGH_QUOTA in SQL_CLR_STRESS runs - // intermittently on paths like D:\DOCUME~1\user\LOCALS~1\Temp\ - - // We do not need to call GetLongPathName if we know it will fail becasue the path does not exist: - int lastErr = Marshal.GetLastWin32Error(); - if (lastErr == Win32Native.ERROR_FILE_NOT_FOUND || lastErr == Win32Native.ERROR_PATH_NOT_FOUND) - doNotTryExpandShortFileName = true; - - return false; - } - - // Safe to copy as we have already done Path.MaxPath bound checking - String.wstrcpy(buffer, shortFileNameBuffer, r); - Length = r; - // We should explicitly null terminate as in some cases the long version of the path - // might actually be shorter than what we started with because of Win32's normalization - // Safe to write directly as bufferLength is guaranteed to be < Path.MaxPath - NullTerminate(); - return true; - } - else { - StringBuilder sb = GetStringBuilder(); - - String origName = sb.ToString(); - String tempName = origName; - bool addedPrefix = false; - if (tempName.Length > Path.MaxPath) { - tempName = Path.AddLongPathPrefix(tempName); - addedPrefix = true; - } - sb.Capacity = m_capacity; - sb.Length = 0; - int r = Win32Native.GetLongPathName(tempName, sb, m_capacity); - - if (r == 0) { - // Note: GetLongPathName will return ERROR_INVALID_FUNCTION on a - // path like \\.\PHYSICALDEVICE0 - some device driver doesn't - // support GetLongPathName on that string. This behavior is - // by design, according to the Core File Services team. - // We also get ERROR_NOT_ENOUGH_QUOTA in SQL_CLR_STRESS runs - // intermittently on paths like D:\DOCUME~1\user\LOCALS~1\Temp\ - - // We do not need to call GetLongPathName if we know it will fail becasue the path does not exist: - int lastErr = Marshal.GetLastWin32Error(); - if (Win32Native.ERROR_FILE_NOT_FOUND == lastErr || Win32Native.ERROR_PATH_NOT_FOUND == lastErr) - doNotTryExpandShortFileName = true; - - sb.Length = 0; - sb.Append(origName); - return false; - } - - if (addedPrefix) - r -= 4; - - // If success, the return buffer length does not account for the terminating null character. - // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. - // If failure, the return buffer length is zero - if (r >= m_maxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - - - sb = Path.RemoveLongPathPrefix(sb); - Length = sb.Length; - return true; - - } - } - - [System.Security.SecurityCritical] - internal unsafe void Fixup(int lenSavedName, int lastSlash) { - if (useStackAlloc) { - char* savedName = stackalloc char[lenSavedName]; - String.wstrcpy(savedName, m_arrayPtr + lastSlash + 1, lenSavedName); - Length = lastSlash; - NullTerminate(); - doNotTryExpandShortFileName = false; - bool r = TryExpandShortFileName(); - // Clean up changes made to the newBuffer. - Append(Path.DirectorySeparatorChar); - if (Length + lenSavedName >= Path.MaxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - String.wstrcpy(m_arrayPtr + Length, savedName, lenSavedName); - Length = Length + lenSavedName; - - } - else { - String savedName = m_sb.ToString(lastSlash + 1, lenSavedName); - Length = lastSlash; - doNotTryExpandShortFileName = false; - bool r = TryExpandShortFileName(); - // Clean up changes made to the newBuffer. - Append(Path.DirectorySeparatorChar); - if (Length + lenSavedName >= m_maxPath) - throw new PathTooLongException(Environment.GetResourceString("IO.PathTooLong")); - m_sb.Append(savedName); - } - } - - [System.Security.SecurityCritical] - internal unsafe bool OrdinalStartsWith(String compareTo, bool ignoreCase) { - if (Length < compareTo.Length) - return false; - - if (useStackAlloc) { - NullTerminate(); - if (ignoreCase) { - String s = new String(m_arrayPtr, 0, compareTo.Length); - return compareTo.Equals(s, StringComparison.OrdinalIgnoreCase); - } - else { - for (int i = 0; i < compareTo.Length; i++) { - if (m_arrayPtr[i] != compareTo[i]) { - return false; - } - } - return true; - } - } - else { - if (ignoreCase) { - return m_sb.ToString().StartsWith(compareTo, StringComparison.OrdinalIgnoreCase); - } - else { - return m_sb.ToString().StartsWith(compareTo, StringComparison.Ordinal); - } - } - } - - [System.Security.SecurityCritical] - private unsafe bool OrdinalEqualsStackAlloc(String compareTo) - { - Contract.Requires(useStackAlloc, "Currently no efficient implementation for StringBuilder.OrdinalEquals(String)"); - - if (Length != compareTo.Length) { - return false; - } - - for (int i = 0; i < compareTo.Length; i++) { - if (m_arrayPtr[i] != compareTo[i]) { - return false; - } - } - - return true; - } - - [System.Security.SecuritySafeCritical] - public override String ToString() { - if (useStackAlloc) { - return new String(m_arrayPtr, 0, Length); - } - else { - return m_sb.ToString(); - } - } - - [System.Security.SecuritySafeCritical] - internal String ToStringOrExisting(String existingString) - { - if (useStackAlloc) { - return OrdinalEqualsStackAlloc(existingString) ? - existingString : - new String(m_arrayPtr, 0, Length); - } - else { - string newString = m_sb.ToString(); // currently no good StringBuilder.OrdinalEquals(string) - return String.Equals(newString, existingString, StringComparison.Ordinal) ? - existingString : - newString; - } - } - - [System.Security.SecurityCritical] - private unsafe char* UnsafeGetArrayPtr() { - Contract.Requires(useStackAlloc, "This should never be called for PathHelpers wrapping a StringBuilder"); - return m_arrayPtr; - } - - private StringBuilder GetStringBuilder() { - Contract.Requires(!useStackAlloc, "This should never be called for PathHelpers that wrap a stackalloc'd buffer"); - return m_sb; - } - - [System.Security.SecurityCritical] - private unsafe void NullTerminate() { - Contract.Requires(useStackAlloc, "This should never be called for PathHelpers wrapping a StringBuilder"); - m_arrayPtr[m_length] = '\0'; - } - - } -} -#endif // FEATURE_PATHCOMPAT
\ No newline at end of file diff --git a/src/mscorlib/src/System/IO/PathInternal.cs b/src/mscorlib/src/System/IO/PathInternal.cs deleted file mode 100644 index 3970e2264a..0000000000 --- a/src/mscorlib/src/System/IO/PathInternal.cs +++ /dev/null @@ -1,806 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using Microsoft.Win32; -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -namespace System.IO -{ - /// <summary>Contains internal path helpers that are shared between many projects.</summary> - internal static class PathInternal - { - internal const string ExtendedPathPrefix = @"\\?\"; - internal const string UncPathPrefix = @"\\"; - internal const string UncExtendedPrefixToInsert = @"?\UNC\"; - internal const string UncExtendedPathPrefix = @"\\?\UNC\"; - internal const string DevicePathPrefix = @"\\.\"; - // \\?\, \\.\, \??\ - internal const int DevicePrefixLength = 4; - // \\ - internal const int UncPrefixLength = 2; - // \\?\UNC\, \\.\UNC\ - internal const int UncExtendedPrefixLength = 8; -#if !PLATFORM_UNIX - internal const int MaxShortPath = 260; - internal const int MaxShortDirectoryPath = 248; -#else - internal const int MaxShortPath = 1024; - internal const int MaxShortDirectoryPath = MaxShortPath; -#endif - - // Windows is limited in long paths by the max size of its internal representation of a unicode string. - // UNICODE_STRING has a max length of USHORT in _bytes_ without a trailing null. - // https://msdn.microsoft.com/en-us/library/windows/hardware/ff564879.aspx - internal const int MaxLongPath = short.MaxValue; - internal static readonly int MaxComponentLength = 255; - -#if !PLATFORM_UNIX - internal static readonly char[] InvalidPathChars = - { - '\"', '<', '>', '|', '\0', - (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, - (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, - (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, - (char)31 - }; -#else - internal static readonly char[] InvalidPathChars = { '\0' }; -#endif - - - /// <summary> - /// Validates volume separator only occurs as C: or \\?\C:. This logic is meant to filter out Alternate Data Streams. - /// </summary> - /// <returns>True if the path has an invalid volume separator.</returns> - internal static bool HasInvalidVolumeSeparator(string path) - { - // Toss out paths with colons that aren't a valid drive specifier. - // Cannot start with a colon and can only be of the form "C:" or "\\?\C:". - // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.) - - // We don't care about skipping starting space for extended paths. Assume no knowledge of extended paths if we're forcing old path behavior. - bool isExtended = -#if FEATURE_PATHCOMPAT - !AppContextSwitches.UseLegacyPathHandling && -#endif - IsExtended(path); - int startIndex = isExtended ? ExtendedPathPrefix.Length : PathStartSkip(path); - - // If we start with a colon - if ((path.Length > startIndex && path[startIndex] == Path.VolumeSeparatorChar) - // Or have an invalid drive letter and colon - || (path.Length >= startIndex + 2 && path[startIndex + 1] == Path.VolumeSeparatorChar && !IsValidDriveChar(path[startIndex])) - // Or have any colons beyond the drive colon - || (path.Length > startIndex + 2 && path.IndexOf(Path.VolumeSeparatorChar, startIndex + 2) != -1)) - { - return true; - } - - return false; - } - - /// <summary> - /// Returns true if the given StringBuilder starts with the given value. - /// </summary> - /// <param name="value">The string to compare against the start of the StringBuilder.</param> - internal static bool StartsWithOrdinal(StringBuilder builder, string value, bool ignoreCase = false) - { - if (value == null || builder.Length < value.Length) - return false; - - if (ignoreCase) - { - for (int i = 0; i < value.Length; i++) - if (char.ToUpperInvariant(builder[i]) != char.ToUpperInvariant(value[i])) return false; - } - else - { - for (int i = 0; i < value.Length; i++) - if (builder[i] != value[i]) return false; - } - - return true; - } - - /// <summary> - /// Returns true if the given character is a valid drive letter - /// </summary> - internal static bool IsValidDriveChar(char value) - { - return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z')); - } - - /// <summary> - /// Returns true if the path is too long - /// </summary> - internal static bool IsPathTooLong(string fullPath) - { - // We'll never know precisely what will fail as paths get changed internally in Windows and - // may grow beyond / shrink below exceed MaxLongPath. -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.BlockLongPaths) - { - // We allow paths of any length if extended (and not in compat mode) - if (AppContextSwitches.UseLegacyPathHandling || !IsExtended(fullPath)) - return fullPath.Length >= MaxShortPath; - } -#endif - - return fullPath.Length >= MaxLongPath; - } - - /// <summary> - /// Return true if any path segments are too long - /// </summary> - internal static bool AreSegmentsTooLong(string fullPath) - { - int length = fullPath.Length; - int lastSeparator = 0; - - for (int i = 0; i < length; i++) - { - if (IsDirectorySeparator(fullPath[i])) - { - if (i - lastSeparator > MaxComponentLength) - return true; - lastSeparator = i; - } - } - - if (length - 1 - lastSeparator > MaxComponentLength) - return true; - - return false; - } - - /// <summary> - /// Returns true if the directory is too long - /// </summary> - internal static bool IsDirectoryTooLong(string fullPath) - { -#if FEATURE_PATHCOMPAT - if (AppContextSwitches.BlockLongPaths) - { - // We allow paths of any length if extended (and not in compat mode) - if (AppContextSwitches.UseLegacyPathHandling || !IsExtended(fullPath)) - return (fullPath.Length >= MaxShortDirectoryPath); - } -#endif - - return IsPathTooLong(fullPath); - } - - /// <summary> - /// Adds the extended path prefix (\\?\) if not relative or already a device path. - /// </summary> - internal static string EnsureExtendedPrefix(string path) - { - // Putting the extended prefix on the path changes the processing of the path. It won't get normalized, which - // means adding to relative paths will prevent them from getting the appropriate current directory inserted. - - // If it already has some variant of a device path (\??\, \\?\, \\.\, //./, etc.) we don't need to change it - // as it is either correct or we will be changing the behavior. When/if Windows supports long paths implicitly - // in the future we wouldn't want normalization to come back and break existing code. - - // In any case, all internal usages should be hitting normalize path (Path.GetFullPath) before they hit this - // shimming method. (Or making a change that doesn't impact normalization, such as adding a filename to a - // normalized base path.) - if (IsPartiallyQualified(path) || IsDevice(path)) - return path; - - // Given \\server\share in longpath becomes \\?\UNC\server\share - if (path.StartsWith(UncPathPrefix, StringComparison.OrdinalIgnoreCase)) - return path.Insert(2, UncExtendedPrefixToInsert); - - return ExtendedPathPrefix + path; - } - - /// <summary> - /// Removes the extended path prefix (\\?\) if present. - /// </summary> - internal static string RemoveExtendedPrefix(string path) - { - if (!IsExtended(path)) - return path; - - // Given \\?\UNC\server\share we return \\server\share - if (IsExtendedUnc(path)) - return path.Remove(2, 6); - - return path.Substring(DevicePrefixLength); - } - - /// <summary> - /// Removes the extended path prefix (\\?\) if present. - /// </summary> - internal static StringBuilder RemoveExtendedPrefix(StringBuilder path) - { - if (!IsExtended(path)) - return path; - - // Given \\?\UNC\server\share we return \\server\share - if (IsExtendedUnc(path)) - return path.Remove(2, 6); - - return path.Remove(0, DevicePrefixLength); - } - - /// <summary> - /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\") - /// </summary> - internal static bool IsDevice(string path) - { - // If the path begins with any two separators it will be recognized and normalized and prepped with - // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. - return IsExtended(path) - || - ( - path.Length >= DevicePrefixLength - && IsDirectorySeparator(path[0]) - && IsDirectorySeparator(path[1]) - && (path[2] == '.' || path[2] == '?') - && IsDirectorySeparator(path[3]) - ); - } - - /// <summary> - /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\") - /// </summary> - internal static bool IsDevice(StringBuffer path) - { - // If the path begins with any two separators it will be recognized and normalized and prepped with - // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not. - return IsExtended(path) - || - ( - path.Length >= DevicePrefixLength - && IsDirectorySeparator(path[0]) - && IsDirectorySeparator(path[1]) - && (path[2] == '.' || path[2] == '?') - && IsDirectorySeparator(path[3]) - ); - } - - /// <summary> - /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the - /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization - /// and path length checks. - /// </summary> - internal static bool IsExtended(string path) - { - // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. - // Skipping of normalization will *only* occur if back slashes ('\') are used. - return path.Length >= DevicePrefixLength - && path[0] == '\\' - && (path[1] == '\\' || path[1] == '?') - && path[2] == '?' - && path[3] == '\\'; - } - - /// <summary> - /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the - /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization - /// and path length checks. - /// </summary> - internal static bool IsExtended(StringBuilder path) - { - // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. - // Skipping of normalization will *only* occur if back slashes ('\') are used. - return path.Length >= DevicePrefixLength - && path[0] == '\\' - && (path[1] == '\\' || path[1] == '?') - && path[2] == '?' - && path[3] == '\\'; - } - - /// <summary> - /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the - /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization - /// and path length checks. - /// </summary> - internal static bool IsExtended(StringBuffer path) - { - // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths. - // Skipping of normalization will *only* occur if back slashes ('\') are used. - return path.Length >= DevicePrefixLength - && path[0] == '\\' - && (path[1] == '\\' || path[1] == '?') - && path[2] == '?' - && path[3] == '\\'; - } - - /// <summary> - /// Returns true if the path uses the extended UNC syntax (\\?\UNC\ or \??\UNC\) - /// </summary> - internal static bool IsExtendedUnc(string path) - { - return path.Length >= UncExtendedPathPrefix.Length - && IsExtended(path) - && char.ToUpper(path[4]) == 'U' - && char.ToUpper(path[5]) == 'N' - && char.ToUpper(path[6]) == 'C' - && path[7] == '\\'; - } - - /// <summary> - /// Returns true if the path uses the extended UNC syntax (\\?\UNC\ or \??\UNC\) - /// </summary> - internal static bool IsExtendedUnc(StringBuilder path) - { - return path.Length >= UncExtendedPathPrefix.Length - && IsExtended(path) - && char.ToUpper(path[4]) == 'U' - && char.ToUpper(path[5]) == 'N' - && char.ToUpper(path[6]) == 'C' - && path[7] == '\\'; - } - - /// <summary> - /// Returns a value indicating if the given path contains invalid characters (", <, >, | - /// NUL, or any ASCII char whose integer representation is in the range of 1 through 31). - /// Does not check for wild card characters ? and *. - /// - /// Will not check if the path is a device path and not in Legacy mode as many of these - /// characters are valid for devices (pipes for example). - /// </summary> - internal static bool HasIllegalCharacters(string path, bool checkAdditional = false) - { - if ( -#if FEATURE_PATHCOMPAT - !AppContextSwitches.UseLegacyPathHandling && -#endif - IsDevice(path)) - { - return false; - } - - return AnyPathHasIllegalCharacters(path, checkAdditional: checkAdditional); - } - - /// <summary> - /// Version of HasIllegalCharacters that checks no AppContextSwitches. Only use if you know you need to skip switches and don't care - /// about proper device path handling. - /// </summary> - internal static bool AnyPathHasIllegalCharacters(string path, bool checkAdditional = false) - { - return path.IndexOfAny(InvalidPathChars) >= 0 -#if !PLATFORM_UNIX - || (checkAdditional && AnyPathHasWildCardCharacters(path)) -#endif - ; - } - - /// <summary> - /// Check for ? and *. - /// </summary> - internal static bool HasWildCardCharacters(string path) - { - // Question mark is part of some device paths - int startIndex = -#if FEATURE_PATHCOMPAT - AppContextSwitches.UseLegacyPathHandling ? 0 : -#endif - IsDevice(path) ? ExtendedPathPrefix.Length : 0; - return AnyPathHasWildCardCharacters(path, startIndex: startIndex); - } - - /// <summary> - /// Version of HasWildCardCharacters that checks no AppContextSwitches. Only use if you know you need to skip switches and don't care - /// about proper device path handling. - /// </summary> - internal static bool AnyPathHasWildCardCharacters(string path, int startIndex = 0) - { - char currentChar; - for (int i = startIndex; i < path.Length; i++) - { - currentChar = path[i]; - if (currentChar == '*' || currentChar == '?') return true; - } - return false; - } - - /// <summary> - /// Gets the length of the root of the path (drive, share, etc.). - /// </summary> - [System.Security.SecuritySafeCritical] - internal unsafe static int GetRootLength(string path) - { - fixed (char* value = path) - { - return (int)GetRootLength(value, (ulong)path.Length); - } - } - - /// <summary> - /// Gets the length of the root of the path (drive, share, etc.). - /// </summary> - [System.Security.SecuritySafeCritical] - internal unsafe static uint GetRootLength(StringBuffer path) - { - if (path.Length == 0) return 0; - return GetRootLength(path.CharPointer, path.Length); - } - - [System.Security.SecurityCritical] - private unsafe static uint GetRootLength(char* path, ulong pathLength) - { - uint i = 0; - -#if PLATFORM_UNIX - if (pathLength >= 1 && (IsDirectorySeparator(path[0]))) - i = 1; -#else - uint volumeSeparatorLength = 2; // Length to the colon "C:" - uint uncRootLength = 2; // Length to the start of the server name "\\" - - bool extendedSyntax = StartsWithOrdinal(path, pathLength, ExtendedPathPrefix); - bool extendedUncSyntax = StartsWithOrdinal(path, pathLength, UncExtendedPathPrefix); - if (extendedSyntax) - { - // Shift the position we look for the root from to account for the extended prefix - if (extendedUncSyntax) - { - // "\\" -> "\\?\UNC\" - uncRootLength = (uint)UncExtendedPathPrefix.Length; - } - else - { - // "C:" -> "\\?\C:" - volumeSeparatorLength += (uint)ExtendedPathPrefix.Length; - } - } - - if ((!extendedSyntax || extendedUncSyntax) && pathLength > 0 && IsDirectorySeparator(path[0])) - { - // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo") - - i = 1; // Drive rooted (\foo) is one character - if (extendedUncSyntax || (pathLength > 1 && IsDirectorySeparator(path[1]))) - { - // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most - // (e.g. to \\?\UNC\Server\Share or \\Server\Share\) - i = uncRootLength; - int n = 2; // Maximum separators to skip - while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++; - } - } - else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == Path.VolumeSeparatorChar) - { - // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:) - // If the colon is followed by a directory separator, move past it - i = volumeSeparatorLength; - if (pathLength >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++; - } -#endif // !PLATFORM_UNIX - return i; - } - - [System.Security.SecurityCritical] - private unsafe static bool StartsWithOrdinal(char* source, ulong sourceLength, string value) - { - if (sourceLength < (ulong)value.Length) return false; - for (int i = 0; i < value.Length; i++) - { - if (value[i] != source[i]) return false; - } - return true; - } - - /// <summary> - /// Returns true if the path specified is relative to the current drive or working directory. - /// Returns false if the path is fixed to a specific drive or UNC path. This method does no - /// validation of the path (URIs will be returned as relative as a result). - /// </summary> - /// <remarks> - /// Handles paths that use the alternate directory separator. It is a frequent mistake to - /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case. - /// "C:a" is drive relative- meaning that it will be resolved against the current directory - /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory - /// will not be used to modify the path). - /// </remarks> - internal static bool IsPartiallyQualified(string path) - { -#if PLATFORM_UNIX - return !(path.Length >= 1 && path[0] == Path.DirectorySeparatorChar); -#else - if (path.Length < 2) - { - // It isn't fixed, it must be relative. There is no way to specify a fixed - // path with one character (or less). - return true; - } - - if (IsDirectorySeparator(path[0])) - { - // There is no valid way to specify a relative path with two initial slashes or - // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ - return !(path[1] == '?' || IsDirectorySeparator(path[1])); - } - - // The only way to specify a fixed path that doesn't begin with two slashes - // is the drive, colon, slash format- i.e. C:\ - return !((path.Length >= 3) - && (path[1] == Path.VolumeSeparatorChar) - && IsDirectorySeparator(path[2]) - // To match old behavior we'll check the drive character for validity as the path is technically - // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream. - && IsValidDriveChar(path[0])); -#endif // !PLATFORM_UNIX - } - - /// <summary> - /// Returns true if the path specified is relative to the current drive or working directory. - /// Returns false if the path is fixed to a specific drive or UNC path. This method does no - /// validation of the path (URIs will be returned as relative as a result). - /// </summary> - /// <remarks> - /// Handles paths that use the alternate directory separator. It is a frequent mistake to - /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case. - /// "C:a" is drive relative- meaning that it will be resolved against the current directory - /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory - /// will not be used to modify the path). - /// </remarks> - internal static bool IsPartiallyQualified(StringBuffer path) - { -#if PLATFORM_UNIX - return !(path.Length >= 1 && path[0] == Path.DirectorySeparatorChar); -#else - if (path.Length < 2) - { - // It isn't fixed, it must be relative. There is no way to specify a fixed - // path with one character (or less). - return true; - } - - if (IsDirectorySeparator(path[0])) - { - // There is no valid way to specify a relative path with two initial slashes or - // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ - return !(path[1] == '?' || IsDirectorySeparator(path[1])); - } - - // The only way to specify a fixed path that doesn't begin with two slashes - // is the drive, colon, slash format- i.e. C:\ - return !((path.Length >= 3) - && (path[1] == Path.VolumeSeparatorChar) - && IsDirectorySeparator(path[2]) - // To match old behavior we'll check the drive character for validity as the path is technically - // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream. - && IsValidDriveChar(path[0])); -#endif // !PLATFORM_UNIX - } - - /// <summary> - /// On Windows, returns the characters to skip at the start of the path if it starts with space(s) and a drive or directory separator. - /// (examples are " C:", " \") - /// This is a legacy behavior of Path.GetFullPath(). - /// </summary> - /// <remarks> - /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip. - /// </remarks> - internal static int PathStartSkip(string path) - { -#if !PLATFORM_UNIX - int startIndex = 0; - while (startIndex < path.Length && path[startIndex] == ' ') startIndex++; - - if (startIndex > 0 && (startIndex < path.Length && IsDirectorySeparator(path[startIndex])) - || (startIndex + 1 < path.Length && path[startIndex + 1] == Path.VolumeSeparatorChar && IsValidDriveChar(path[startIndex]))) - { - // Go ahead and skip spaces as we're either " C:" or " \" - return startIndex; - } -#endif - - return 0; - } - - /// <summary> - /// True if the given character is a directory separator. - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsDirectorySeparator(char c) - { - return c == Path.DirectorySeparatorChar -#if !PLATFORM_UNIX - || c == Path.AltDirectorySeparatorChar -#endif - ; - } - - /// <summary> - /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present. - /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip). - /// - /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false. - /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as - /// such can't be used here (and is overkill for our uses). - /// - /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments. - /// </summary> - /// <remarks> - /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do - /// not need trimming of trailing whitespace here. - /// - /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization. - /// - /// For legacy desktop behavior with ExpandShortPaths: - /// - It has no impact on GetPathRoot() so doesn't need consideration. - /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share). - /// - /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was - /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you - /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by - /// this undocumented behavior. - /// </remarks> - internal static string NormalizeDirectorySeparators(string path) - { - if (string.IsNullOrEmpty(path)) return path; - - char current; - int start = PathStartSkip(path); - - if (start == 0) - { - // Make a pass to see if we need to normalize so we can potentially skip allocating - bool normalized = true; - - for (int i = 0; i < path.Length; i++) - { - current = path[i]; - if (IsDirectorySeparator(current) - && (current != Path.DirectorySeparatorChar -#if !PLATFORM_UNIX - // Check for sequential separators past the first position (we need to keep initial two for UNC/extended) - || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1])) -#endif - )) - { - normalized = false; - break; - } - } - - if (normalized) return path; - } - - StringBuilder builder = StringBuilderCache.Acquire(path.Length); - -#if !PLATFORM_UNIX - // On Windows we always keep the first separator, even if the next is a separator (we need to keep initial two for UNC/extended) - if (IsDirectorySeparator(path[start])) - { - start++; - builder.Append(Path.DirectorySeparatorChar); - } -#endif - - for (int i = start; i < path.Length; i++) - { - current = path[i]; - - // If we have a separator - if (IsDirectorySeparator(current)) - { - // If the next is a separator, skip adding this - if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])) - { - continue; - } - - // Ensure it is the primary separator - current = Path.DirectorySeparatorChar; - } - - builder.Append(current); - } - - return StringBuilderCache.GetStringAndRelease(builder); - } - -#if PLATFORM_UNIX - // We rely on Windows to remove relative segments on Windows. This would need to be updated to - // handle the proper rooting on Windows if we for some reason need it. - - /// <summary> - /// Try to remove relative segments from the given path (without combining with a root). - /// </summary> - /// <param name="skip">Skip the specified number of characters before evaluating.</param> - internal static string RemoveRelativeSegments(string path, int skip = 0) - { - bool flippedSeparator = false; - - // Remove "//", "/./", and "/../" from the path by copying each character to the output, - // except the ones we're removing, such that the builder contains the normalized path - // at the end. - var sb = StringBuilderCache.Acquire(path.Length); - if (skip > 0) - { - sb.Append(path, 0, skip); - } - - int componentCharCount = 0; - for (int i = skip; i < path.Length; i++) - { - char c = path[i]; - - if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length) - { - componentCharCount = 0; - - // Skip this character if it's a directory separator and if the next character is, too, - // e.g. "parent//child" => "parent/child" - if (PathInternal.IsDirectorySeparator(path[i + 1])) - { - continue; - } - - // Skip this character and the next if it's referring to the current directory, - // e.g. "parent/./child" =? "parent/child" - if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) && - path[i + 1] == '.') - { - i++; - continue; - } - - // Skip this character and the next two if it's referring to the parent directory, - // e.g. "parent/child/../grandchild" => "parent/grandchild" - if (i + 2 < path.Length && - (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) && - path[i + 1] == '.' && path[i + 2] == '.') - { - // Unwind back to the last slash (and if there isn't one, clear out everything). - int s; - for (s = sb.Length - 1; s >= 0; s--) - { - if (PathInternal.IsDirectorySeparator(sb[s])) - { - sb.Length = s; - break; - } - } - if (s < 0) - { - sb.Length = 0; - } - - i += 2; - continue; - } - } - - if (++componentCharCount > PathInternal.MaxComponentLength) - { - throw new PathTooLongException(); - } - - // Normalize the directory separator if needed - if (c != Path.DirectorySeparatorChar && c == Path.AltDirectorySeparatorChar) - { - c = Path.DirectorySeparatorChar; - flippedSeparator = true; - } - - sb.Append(c); - } - - if (flippedSeparator || sb.Length != path.Length) - { - return StringBuilderCache.GetStringAndRelease(sb); - } - else - { - // We haven't changed the source path, return the original - StringBuilderCache.Release(sb); - return path; - } - } -#endif // PLATFORM_UNIX - } -}
\ No newline at end of file diff --git a/src/mscorlib/src/System/IO/PinnedBufferMemoryStream.cs b/src/mscorlib/src/System/IO/PinnedBufferMemoryStream.cs index 4fd54f57f5..890669fa58 100644 --- a/src/mscorlib/src/System/IO/PinnedBufferMemoryStream.cs +++ b/src/mscorlib/src/System/IO/PinnedBufferMemoryStream.cs @@ -15,6 +15,7 @@ ===========================================================*/ using System; using System.Runtime.InteropServices; +using System.Diagnostics; using System.Diagnostics.Contracts; namespace System.IO { @@ -24,13 +25,11 @@ namespace System.IO { private GCHandle _pinningHandle; // The new inheritance model requires a Critical default ctor since base (UnmanagedMemoryStream) has one - [System.Security.SecurityCritical] private PinnedBufferMemoryStream():base(){} - [System.Security.SecurityCritical] // auto-generated internal PinnedBufferMemoryStream(byte[] array) { - Contract.Assert(array != null, "Array can't be null"); + Debug.Assert(array != null, "Array can't be null"); int len = array.Length; // Handle 0 length byte arrays specially. @@ -52,7 +51,6 @@ namespace System.IO { Dispose(false); } - [System.Security.SecuritySafeCritical] // auto-generated protected override void Dispose(bool disposing) { if (_isOpen) { diff --git a/src/mscorlib/src/System/IO/Stream.cs b/src/mscorlib/src/System/IO/Stream.cs index a1f29364cb..3cdfad613e 100644 --- a/src/mscorlib/src/System/IO/Stream.cs +++ b/src/mscorlib/src/System/IO/Stream.cs @@ -15,26 +15,23 @@ ** ===========================================================*/ using System; +using System.Buffers; using System.Threading; using System.Threading.Tasks; - using System.Runtime; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Security; using System.Security.Permissions; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Reflection; namespace System.IO { [Serializable] [ComVisible(true)] -#if FEATURE_REMOTING public abstract class Stream : MarshalByRefObject, IDisposable { -#else // FEATURE_REMOTING - public abstract class Stream : IDisposable { -#endif // FEATURE_REMOTING public static readonly Stream Null = new NullStream(); @@ -112,13 +109,11 @@ namespace System.IO { } } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task CopyToAsync(Stream destination) { int bufferSize = _DefaultCopyBufferSize; -#if FEATURE_CORECLR if (CanSeek) { long length = Length; @@ -147,23 +142,20 @@ namespace System.IO { bufferSize = (int)Math.Min(bufferSize, remaining); } } -#endif // FEATURE_CORECLR - + return CopyToAsync(destination, bufferSize); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task CopyToAsync(Stream destination, Int32 bufferSize) { return CopyToAsync(destination, bufferSize, CancellationToken.None); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) { - ValidateCopyToArguments(destination, bufferSize); + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); return CopyToAsyncInternal(destination, bufferSize, cancellationToken); } @@ -175,11 +167,22 @@ namespace System.IO { Contract.Requires(CanRead); Contract.Requires(destination.CanWrite); - byte[] buffer = new byte[bufferSize]; - int bytesRead; - while ((bytesRead = await ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) + byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize); + bufferSize = 0; // reuse same field for high water mark to avoid needing another field in the state machine + try + { + while (true) + { + int bytesRead = await ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false); + if (bytesRead == 0) break; + if (bytesRead > bufferSize) bufferSize = bytesRead; + await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + } + } + finally { - await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); + Array.Clear(buffer, 0, bufferSize); // clear only the most we used + ArrayPool<byte>.Shared.Return(buffer, clearArray: false); } } @@ -190,7 +193,6 @@ namespace System.IO { { int bufferSize = _DefaultCopyBufferSize; -#if FEATURE_CORECLR if (CanSeek) { long length = Length; @@ -209,19 +211,30 @@ namespace System.IO { bufferSize = (int)Math.Min(bufferSize, remaining); } } -#endif // FEATURE_CORECLR - + CopyTo(destination, bufferSize); } public virtual void CopyTo(Stream destination, int bufferSize) { - ValidateCopyToArguments(destination, bufferSize); - - byte[] buffer = new byte[bufferSize]; - int read; - while ((read = Read(buffer, 0, buffer.Length)) != 0) - destination.Write(buffer, 0, read); + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize); + int highwaterMark = 0; + try + { + int read; + while ((read = Read(buffer, 0, buffer.Length)) != 0) + { + if (read > highwaterMark) highwaterMark = read; + destination.Write(buffer, 0, read); + } + } + finally + { + Array.Clear(buffer, 0, highwaterMark); // clear only the most we used + ArrayPool<byte>.Shared.Return(buffer, clearArray: false); + } } // Stream used to require that all cleanup logic went into Close(), @@ -265,14 +278,12 @@ namespace System.IO { public abstract void Flush(); - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public Task FlushAsync() { return FlushAsync(CancellationToken.None); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public virtual Task FlushAsync(CancellationToken cancellationToken) { @@ -287,14 +298,12 @@ namespace System.IO { return new ManualResetEvent(false); } - [HostProtection(ExternalThreading=true)] public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { Contract.Ensures(Contract.Result<IAsyncResult>() != null); return BeginReadInternal(buffer, offset, count, callback, state, serializeAsynchronously: false, apm: true); } - [HostProtection(ExternalThreading = true)] internal IAsyncResult BeginReadInternal( byte[] buffer, int offset, int count, AsyncCallback callback, Object state, bool serializeAsynchronously, bool apm) @@ -326,7 +335,7 @@ namespace System.IO { // As we're currently inside of it, we can get the current task // and grab the parameters from it. var thisTask = Task.InternalCurrent as ReadWriteTask; - Contract.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask"); + Debug.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask"); try { @@ -360,7 +369,7 @@ namespace System.IO { public virtual int EndRead(IAsyncResult asyncResult) { if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.Ensures(Contract.Result<int>() >= 0); Contract.EndContractBlock(); @@ -389,14 +398,12 @@ namespace System.IO { } } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task<int> ReadAsync(Byte[] buffer, int offset, int count) { return ReadAsync(buffer, offset, count, CancellationToken.None); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { @@ -407,7 +414,6 @@ namespace System.IO { : BeginEndReadAsync(buffer, offset, count); } - [System.Security.SecuritySafeCritical] [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern bool HasOverriddenBeginEndRead(); @@ -436,14 +442,12 @@ namespace System.IO { - [HostProtection(ExternalThreading=true)] public virtual IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { Contract.Ensures(Contract.Result<IAsyncResult>() != null); return BeginWriteInternal(buffer, offset, count, callback, state, serializeAsynchronously: false, apm: true); } - [HostProtection(ExternalThreading = true)] internal IAsyncResult BeginWriteInternal( byte[] buffer, int offset, int count, AsyncCallback callback, Object state, bool serializeAsynchronously, bool apm) @@ -475,7 +479,7 @@ namespace System.IO { // As we're currently inside of it, we can get the current task // and grab the parameters from it. var thisTask = Task.InternalCurrent as ReadWriteTask; - Contract.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask"); + Debug.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask"); try { @@ -508,20 +512,20 @@ namespace System.IO { private void RunReadWriteTaskWhenReady(Task asyncWaiter, ReadWriteTask readWriteTask) { - Contract.Assert(readWriteTask != null); // Should be Contract.Requires, but CCRewrite is doing a poor job with + Debug.Assert(readWriteTask != null); // Should be Contract.Requires, but CCRewrite is doing a poor job with // preconditions in async methods that await. - Contract.Assert(asyncWaiter != null); // Ditto + Debug.Assert(asyncWaiter != null); // Ditto // If the wait has already completed, run the task. if (asyncWaiter.IsCompleted) { - Contract.Assert(asyncWaiter.IsRanToCompletion, "The semaphore wait should always complete successfully."); + Debug.Assert(asyncWaiter.IsRanToCompletion, "The semaphore wait should always complete successfully."); RunReadWriteTask(readWriteTask); } else // Otherwise, wait for our turn, and then run the task. { asyncWaiter.ContinueWith((t, state) => { - Contract.Assert(t.IsRanToCompletion, "The semaphore wait should always complete successfully."); + Debug.Assert(t.IsRanToCompletion, "The semaphore wait should always complete successfully."); var rwt = (ReadWriteTask)state; rwt._stream.RunReadWriteTask(rwt); // RunReadWriteTask(readWriteTask); }, readWriteTask, default(CancellationToken), TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); @@ -531,7 +535,7 @@ namespace System.IO { private void RunReadWriteTask(ReadWriteTask readWriteTask) { Contract.Requires(readWriteTask != null); - Contract.Assert(_activeReadWriteTask == null, "Expected no other readers or writers"); + Debug.Assert(_activeReadWriteTask == null, "Expected no other readers or writers"); // Schedule the task. ScheduleAndStart must happen after the write to _activeReadWriteTask to avoid a race. // Internally, we're able to directly call ScheduleAndStart rather than Start, avoiding @@ -545,14 +549,14 @@ namespace System.IO { private void FinishTrackingAsyncOperation() { _activeReadWriteTask = null; - Contract.Assert(_asyncActiveSemaphore != null, "Must have been initialized in order to get here."); + Debug.Assert(_asyncActiveSemaphore != null, "Must have been initialized in order to get here."); _asyncActiveSemaphore.Release(); } public virtual void EndWrite(IAsyncResult asyncResult) { if (asyncResult==null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.EndContractBlock(); var writeTask = _activeReadWriteTask; @@ -572,7 +576,7 @@ namespace System.IO { try { writeTask.GetAwaiter().GetResult(); // block until completion, then propagate any exceptions - Contract.Assert(writeTask.Status == TaskStatus.RanToCompletion); + Debug.Assert(writeTask.Status == TaskStatus.RanToCompletion); } finally { @@ -613,7 +617,6 @@ namespace System.IO { _buffer = null; } - [SecuritySafeCritical] // necessary for EC.Capture [MethodImpl(MethodImplOptions.NoInlining)] public ReadWriteTask( bool isRead, @@ -651,7 +654,6 @@ namespace System.IO { } } - [SecurityCritical] // necessary for CoreCLR private static void InvokeAsyncCallback(object completedTask) { var rwc = (ReadWriteTask)completedTask; @@ -660,10 +662,8 @@ namespace System.IO { callback(rwc); } - [SecurityCritical] // necessary for CoreCLR private static ContextCallback s_invokeAsyncCallback; - [SecuritySafeCritical] // necessary for ExecutionContext.Run void ITaskCompletionAction.Invoke(Task completingTask) { // Get the ExecutionContext. If there is none, just run the callback @@ -690,7 +690,6 @@ namespace System.IO { bool ITaskCompletionAction.InvokeMayRunArbitraryCode { get { return true; } } } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task WriteAsync(Byte[] buffer, int offset, int count) { @@ -699,7 +698,6 @@ namespace System.IO { - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken) { @@ -710,7 +708,6 @@ namespace System.IO { : BeginEndWriteAsync(buffer, offset, count); } - [System.Security.SecuritySafeCritical] [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern bool HasOverriddenBeginEndWrite(); @@ -772,11 +769,10 @@ namespace System.IO { Write(oneByteArray, 0, 1); } - [HostProtection(Synchronization=true)] public static Stream Synchronized(Stream stream) { if (stream==null) - throw new ArgumentNullException("stream"); + throw new ArgumentNullException(nameof(stream)); Contract.Ensures(Contract.Result<Stream>() != null); Contract.EndContractBlock(); if (stream is SyncStream) @@ -853,23 +849,6 @@ namespace System.IO { { SynchronousAsyncResult.EndWrite(asyncResult); } - - internal void ValidateCopyToArguments(Stream destination, int bufferSize) - { - if (destination == null) - throw new ArgumentNullException("destination"); - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); - if (!CanRead && !CanWrite) - throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed")); - if (!destination.CanRead && !destination.CanWrite) - throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed")); - if (!CanRead) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream")); - if (!destination.CanWrite) - throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream")); - Contract.EndContractBlock(); - } [Serializable] private sealed class NullStream : Stream @@ -900,11 +879,18 @@ namespace System.IO { set {} } + public override void CopyTo(Stream destination, int bufferSize) + { + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); + + // After we validate arguments this is a nop. + } + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { // Validate arguments here for compat, since previously this method // was inherited from Stream (which did check its arguments). - ValidateCopyToArguments(destination, bufferSize); + StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize); return cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) : @@ -928,7 +914,6 @@ namespace System.IO { Task.CompletedTask; } - [HostProtection(ExternalThreading = true)] public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { if (!CanRead) __Error.ReadNotSupported(); @@ -939,13 +924,12 @@ namespace System.IO { public override int EndRead(IAsyncResult asyncResult) { if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.EndContractBlock(); return BlockingEndRead(asyncResult); } - [HostProtection(ExternalThreading = true)] public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { if (!CanWrite) __Error.WriteNotSupported(); @@ -956,7 +940,7 @@ namespace System.IO { public override void EndWrite(IAsyncResult asyncResult) { if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.EndContractBlock(); BlockingEndWrite(asyncResult); @@ -1102,7 +1086,7 @@ namespace System.IO { internal SyncStream(Stream stream) { if (stream == null) - throw new ArgumentNullException("stream"); + throw new ArgumentNullException(nameof(stream)); Contract.EndContractBlock(); _stream = stream; } @@ -1217,7 +1201,6 @@ namespace System.IO { return _stream.ReadByte(); } - [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { bool overridesBeginRead = _stream.HasOverriddenBeginEndRead(); @@ -1239,7 +1222,7 @@ namespace System.IO { public override int EndRead(IAsyncResult asyncResult) { if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.Ensures(Contract.Result<int>() >= 0); Contract.EndContractBlock(); @@ -1271,7 +1254,6 @@ namespace System.IO { _stream.WriteByte(b); } - [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) { bool overridesBeginWrite = _stream.HasOverriddenBeginEndWrite(); @@ -1293,7 +1275,7 @@ namespace System.IO { public override void EndWrite(IAsyncResult asyncResult) { if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); + throw new ArgumentNullException(nameof(asyncResult)); Contract.EndContractBlock(); lock(_stream) diff --git a/src/mscorlib/src/System/IO/StreamHelpers.CopyValidation.cs b/src/mscorlib/src/System/IO/StreamHelpers.CopyValidation.cs new file mode 100644 index 0000000000..8ff0e045ca --- /dev/null +++ b/src/mscorlib/src/System/IO/StreamHelpers.CopyValidation.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.IO +{ + /// <summary>Provides methods to help in the implementation of Stream-derived types.</summary> + internal static partial class StreamHelpers + { + /// <summary>Validate the arguments to CopyTo, as would Stream.CopyTo.</summary> + public static void ValidateCopyToArgs(Stream source, Stream destination, int bufferSize) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + } + + bool sourceCanRead = source.CanRead; + if (!sourceCanRead && !source.CanWrite) + { + throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed")); + } + + bool destinationCanWrite = destination.CanWrite; + if (!destinationCanWrite && !destination.CanRead) + { + throw new ObjectDisposedException(nameof(destination), Environment.GetResourceString("ObjectDisposed_StreamClosed")); + } + + if (!sourceCanRead) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream")); + } + + if (!destinationCanWrite) + { + throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream")); + } + } + } +} diff --git a/src/mscorlib/src/System/IO/StreamReader.cs b/src/mscorlib/src/System/IO/StreamReader.cs index 549733bb47..708db088e8 100644 --- a/src/mscorlib/src/System/IO/StreamReader.cs +++ b/src/mscorlib/src/System/IO/StreamReader.cs @@ -2,22 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** -** -** Purpose: For reading text from streams in a particular -** encoding. -** -** -===========================================================*/ - -using System; using System.Text; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Security.Permissions; @@ -161,11 +149,11 @@ namespace System.IO public StreamReader(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool leaveOpen) { if (stream == null || encoding == null) - throw new ArgumentNullException((stream == null ? "stream" : "encoding")); + throw new ArgumentNullException((stream == null ? nameof(stream) : nameof(encoding))); if (!stream.CanRead) throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable")); if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException(nameof(bufferSize), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); Contract.EndContractBlock(); Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, leaveOpen); @@ -187,26 +175,20 @@ namespace System.IO : this(path, encoding, detectEncodingFromByteOrderMarks, DefaultBufferSize) { } - [System.Security.SecuritySafeCritical] public StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize) - : this(path, encoding, detectEncodingFromByteOrderMarks, bufferSize, true) { - } - - [System.Security.SecurityCritical] - internal StreamReader(String path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int bufferSize, bool checkHost) { // Don't open a Stream before checking for invalid arguments, // or we'll create a FileStream on disk and we won't close it until // the finalizer runs, causing problems for applications. if (path==null || encoding==null) - throw new ArgumentNullException((path==null ? "path" : "encoding")); + throw new ArgumentNullException((path==null ? nameof(path) : nameof(encoding))); if (path.Length==0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException(nameof(bufferSize), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); Contract.EndContractBlock(); - Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost); + Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false); Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize, false); } @@ -358,9 +340,9 @@ namespace System.IO public override int Read([In, Out] char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -416,9 +398,9 @@ namespace System.IO public override int ReadBlock([In, Out] char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -434,7 +416,7 @@ namespace System.IO // Trims n bytes from the front of the buffer. private void CompressBuffer(int n) { - Contract.Assert(byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?"); + Debug.Assert(byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?"); Buffer.InternalBlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n); byteLen -= n; } @@ -502,7 +484,7 @@ namespace System.IO if (!_checkPreamble) return _checkPreamble; - Contract.Assert(bytePos <= _preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos <= _preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?"); int len = (byteLen >= (_preamble.Length))? (_preamble.Length - bytePos) : (byteLen - bytePos); for(int i=0; i<len; i++, bytePos++) { @@ -513,7 +495,7 @@ namespace System.IO } } - Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); if (_checkPreamble) { if (bytePos == _preamble.Length) { @@ -568,9 +550,9 @@ namespace System.IO byteLen = 0; do { if (_checkPreamble) { - Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos); - Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (len == 0) { // EOF but we might have buffered bytes from previous @@ -588,9 +570,9 @@ namespace System.IO byteLen += len; } else { - Contract.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length); - Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (byteLen == 0) // We're at EOF return charLen; @@ -650,12 +632,12 @@ namespace System.IO readToUserBuffer = desiredChars >= _maxCharsPerBuffer; do { - Contract.Assert(charsRead == 0); + Debug.Assert(charsRead == 0); if (_checkPreamble) { - Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos); - Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (len == 0) { // EOF but we might have buffered bytes from previous @@ -677,11 +659,11 @@ namespace System.IO byteLen += len; } else { - Contract.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length); - Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (byteLen == 0) // EOF break; @@ -775,7 +757,6 @@ namespace System.IO } #region Task based Async APIs - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task<String> ReadLineAsync() { @@ -856,7 +837,6 @@ namespace System.IO return GetStringAndReleaseSharedStringBuilder(sb); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task<String> ReadToEndAsync() { @@ -893,14 +873,13 @@ namespace System.IO return GetStringAndReleaseSharedStringBuilder(sb); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task<int> ReadAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -957,14 +936,14 @@ namespace System.IO // We break out of the loop if the stream is blocked (EOF is reached). do { - Contract.Assert(n == 0); + Debug.Assert(n == 0); if (CheckPreamble_Prop) { - Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); int tmpBytePos = BytePos_Prop; int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false); - Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (len == 0) { @@ -985,7 +964,7 @@ namespace System.IO } // How can part of the preamble yield any chars? - Contract.Assert(n == 0); + Debug.Assert(n == 0); IsBlocked_Prop = true; break; @@ -997,11 +976,11 @@ namespace System.IO } else { - Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false); - Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (ByteLen_Prop == 0) // EOF { @@ -1031,7 +1010,7 @@ namespace System.IO readToUserBuffer = count >= MaxCharsPerBuffer_Prop; } - Contract.Assert(n == 0); + Debug.Assert(n == 0); CharPos_Prop = 0; if (readToUserBuffer) @@ -1039,7 +1018,7 @@ namespace System.IO n += Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, buffer, index + charsRead); // Why did the bytes yield no chars? - Contract.Assert(n > 0); + Debug.Assert(n > 0); CharLen_Prop = 0; // StreamReader's buffer is empty. } @@ -1048,7 +1027,7 @@ namespace System.IO n = Decoder_Prop.GetChars(tmpByteBuffer, 0, ByteLen_Prop, CharBuffer_Prop, 0); // Why did the bytes yield no chars? - Contract.Assert(n > 0); + Debug.Assert(n > 0); CharLen_Prop += n; // Number of chars in StreamReader's buffer. } @@ -1081,14 +1060,13 @@ namespace System.IO return charsRead; } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -1185,10 +1163,10 @@ namespace System.IO ByteLen_Prop = 0; do { if (CheckPreamble_Prop) { - Contract.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(BytePos_Prop <= Preamble_Prop.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?"); int tmpBytePos = BytePos_Prop; int len = await tmpStream.ReadAsync(tmpByteBuffer, tmpBytePos, tmpByteBuffer.Length - tmpBytePos).ConfigureAwait(false); - Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); + Debug.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class."); if (len == 0) { // EOF but we might have buffered bytes from previous @@ -1206,9 +1184,9 @@ namespace System.IO ByteLen_Prop += len; } else { - Contract.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); + Debug.Assert(BytePos_Prop == 0, "_bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?"); ByteLen_Prop = await tmpStream.ReadAsync(tmpByteBuffer, 0, tmpByteBuffer.Length).ConfigureAwait(false); - Contract.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! Bug in stream class."); + Debug.Assert(ByteLen_Prop >= 0, "Stream.Read returned a negative number! Bug in stream class."); if (ByteLen_Prop == 0) // We're at EOF return CharLen_Prop; diff --git a/src/mscorlib/src/System/IO/StreamWriter.cs b/src/mscorlib/src/System/IO/StreamWriter.cs index 65613bb0a6..22eba82605 100644 --- a/src/mscorlib/src/System/IO/StreamWriter.cs +++ b/src/mscorlib/src/System/IO/StreamWriter.cs @@ -2,25 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** -** -** Purpose: For writing text to streams in a particular -** encoding. -** -** -===========================================================*/ -using System; using System.Text; -using System.Threading; -using System.Globalization; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using System.Security.Permissions; using System.Runtime.Serialization; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -45,10 +31,8 @@ namespace System.IO private const int DefaultFileStreamBufferSize = 4096; private const int MinBufferSize = 128; - private const Int32 DontCopyOnWriteLineThreshold = 512; - // Bit bucket - Null has no backing store. Non closable. - public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, new UTF8Encoding(false, true), MinBufferSize, true); + public new static readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, true); private Stream stream; private Encoding encoding; @@ -61,12 +45,6 @@ namespace System.IO private bool haveWrittenPreamble; private bool closable; -#if MDA_SUPPORTED - [NonSerialized] - // For StreamWriterBufferedDataLost MDA - private MdaHelper mdaHelper; -#endif - // We don't guarantee thread safety on StreamWriter, but we should at // least prevent users from trying to write anything while an Async // write from the same thread is in progress. @@ -93,20 +71,9 @@ namespace System.IO // Even Close() will hit the exception as it would try to flush the unwritten data. // Maybe we can add a DiscardBufferedData() method to get out of such situation (like // StreamReader though for different reason). Either way, the buffered data will be lost! - private static volatile Encoding _UTF8NoBOM; - internal static Encoding UTF8NoBOM { [FriendAccessAllowed] - get { - if (_UTF8NoBOM == null) { - // No need for double lock - we just want to avoid extra - // allocations in the common case. - UTF8Encoding noBOM = new UTF8Encoding(false, true); - Thread.MemoryBarrier(); - _UTF8NoBOM = noBOM; - } - return _UTF8NoBOM; - } + get { return EncodingCache.UTF8NoBOM; } } @@ -133,10 +100,10 @@ namespace System.IO : base(null) // Ask for CurrentCulture all the time { if (stream == null || encoding == null) - throw new ArgumentNullException((stream == null ? "stream" : "encoding")); + throw new ArgumentNullException((stream == null ? nameof(stream) : nameof(encoding))); if (!stream.CanWrite) throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable")); - if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); Contract.EndContractBlock(); Init(stream, encoding, bufferSize, leaveOpen); @@ -154,28 +121,23 @@ namespace System.IO : this(path, append, encoding, DefaultBufferSize) { } - [System.Security.SecuritySafeCritical] - public StreamWriter(String path, bool append, Encoding encoding, int bufferSize): this(path, append, encoding, bufferSize, true) { - } - - [System.Security.SecurityCritical] - internal StreamWriter(String path, bool append, Encoding encoding, int bufferSize, bool checkHost) + public StreamWriter(String path, bool append, Encoding encoding, int bufferSize) : base(null) - { // Ask for CurrentCulture all the time + { + // Ask for CurrentCulture all the time if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (encoding == null) - throw new ArgumentNullException("encoding"); + throw new ArgumentNullException(nameof(encoding)); if (path.Length == 0) throw new ArgumentException(Environment.GetResourceString("Argument_EmptyPath")); - if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); Contract.EndContractBlock(); - Stream stream = CreateFile(path, append, checkHost); + Stream stream = CreateFile(path, append); Init(stream, encoding, bufferSize, false); } - [System.Security.SecuritySafeCritical] private void Init(Stream streamArg, Encoding encodingArg, int bufferSize, bool shouldLeaveOpen) { this.stream = streamArg; @@ -190,21 +152,12 @@ namespace System.IO if (stream.CanSeek && stream.Position > 0) haveWrittenPreamble = true; closable = !shouldLeaveOpen; -#if MDA_SUPPORTED - if (Mda.StreamWriterBufferedDataLost.Enabled) { - String callstack = null; - if (Mda.StreamWriterBufferedDataLost.CaptureAllocatedCallStack) - callstack = Environment.GetStackTrace(null, false); - mdaHelper = new MdaHelper(this, callstack); - } -#endif } - [System.Security.SecurityCritical] - private static Stream CreateFile(String path, bool append, bool checkHost) { + private static Stream CreateFile(String path, bool append) { FileMode mode = append? FileMode.Append: FileMode.Create; FileStream f = new FileStream(path, mode, FileAccess.Write, FileShare.Read, - DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false, checkHost); + DefaultFileStreamBufferSize, FileOptions.SequentialScan, Path.GetFileName(path), false, false); return f; } @@ -221,20 +174,11 @@ namespace System.IO // is generally the right thing to do. if (stream != null) { // Note: flush on the underlying stream can throw (ex., low disk space) -#if FEATURE_CORECLR if (disposing) -#else - if (disposing || (LeaveOpen && stream is __ConsoleStream)) -#endif { CheckAsyncTaskInProgress(); Flush(true, true); -#if MDA_SUPPORTED - // Disable buffered data loss mda - if (mdaHelper != null) - GC.SuppressFinalize(mdaHelper); -#endif } } } @@ -354,7 +298,7 @@ namespace System.IO if (charPos == charLen) Flush(false, false); int n = charLen - charPos; if (n > count) n = count; - Contract.Assert(n > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race condition in user code."); + Debug.Assert(n > 0, "StreamWriter::Write(char[]) isn't making progress! This is most likely a race condition in user code."); Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char)); charPos += n; index += n; @@ -365,11 +309,11 @@ namespace System.IO public override void Write(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -380,7 +324,7 @@ namespace System.IO if (charPos == charLen) Flush(false, false); int n = charLen - charPos; if (n > count) n = count; - Contract.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); + Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char)); charPos += n; index += n; @@ -402,7 +346,7 @@ namespace System.IO if (charPos == charLen) Flush(false, false); int n = charLen - charPos; if (n > count) n = count; - Contract.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); + Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); value.CopyTo(index, charBuffer, charPos, n); charPos += n; index += n; @@ -413,7 +357,6 @@ namespace System.IO } #region Task based Async APIs - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteAsync(char value) { @@ -444,7 +387,7 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } @@ -457,7 +400,7 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } @@ -468,14 +411,13 @@ namespace System.IO if (autoFlush) { await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } _this.CharPos_Prop = charPos; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteAsync(String value) { @@ -520,7 +462,7 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } @@ -528,7 +470,7 @@ namespace System.IO if (n > count) n = count; - Contract.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); + Debug.Assert(n > 0, "StreamWriter::Write(String) isn't making progress! This is most likely a race condition in user code."); value.CopyTo(index, charBuffer, charPos, n); @@ -543,7 +485,7 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } @@ -554,23 +496,22 @@ namespace System.IO if (autoFlush) { await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } _this.CharPos_Prop = charPos; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -609,14 +550,14 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } int n = charLen - charPos; if (n > count) n = count; - Contract.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); + Debug.Assert(n > 0, "StreamWriter::Write(char[], int, int) isn't making progress! This is most likely a race condition in user code."); Buffer.InternalBlockCopy(buffer, index * sizeof(char), charBuffer, charPos * sizeof(char), n * sizeof(char)); @@ -631,7 +572,7 @@ namespace System.IO { if (charPos == charLen) { await _this.FlushAsyncInternal(false, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } @@ -642,14 +583,13 @@ namespace System.IO if (autoFlush) { await _this.FlushAsyncInternal(true, false, charBuffer, charPos).ConfigureAwait(false); - Contract.Assert(_this.charPos == 0); + Debug.Assert(_this.charPos == 0); charPos = 0; } _this.CharPos_Prop = charPos; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteLineAsync() { @@ -672,7 +612,6 @@ namespace System.IO } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteLineAsync(char value) { @@ -695,7 +634,6 @@ namespace System.IO } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteLineAsync(String value) { @@ -718,16 +656,15 @@ namespace System.IO } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteLineAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -751,7 +688,6 @@ namespace System.IO } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task FlushAsync() { @@ -825,42 +761,5 @@ namespace System.IO await stream.FlushAsync().ConfigureAwait(false); } #endregion - -#if MDA_SUPPORTED - // StreamWriterBufferedDataLost MDA - // Instead of adding a finalizer to StreamWriter for detecting buffered data loss - // (ie, when the user forgets to call Close/Flush on the StreamWriter), we will - // have a separate object with normal finalization semantics that maintains a - // back pointer to this StreamWriter and alerts about any data loss - private sealed class MdaHelper - { - private StreamWriter streamWriter; - private String allocatedCallstack; // captures the callstack when this streamwriter was allocated - - internal MdaHelper(StreamWriter sw, String cs) - { - streamWriter = sw; - allocatedCallstack = cs; - } - - // Finalizer - ~MdaHelper() - { - // Make sure people closed this StreamWriter, exclude StreamWriter::Null. - if (streamWriter.charPos != 0 && streamWriter.stream != null && streamWriter.stream != Stream.Null) { - String fileName = (streamWriter.stream is FileStream) ? ((FileStream)streamWriter.stream).NameInternal : "<unknown>"; - String callStack = allocatedCallstack; - - if (callStack == null) - callStack = Environment.GetResourceString("IO_StreamWriterBufferedDataLostCaptureAllocatedFromCallstackNotEnabled"); - - String message = Environment.GetResourceString("IO_StreamWriterBufferedDataLost", streamWriter.stream.GetType().FullName, fileName, callStack); - - Mda.StreamWriterBufferedDataLost.ReportError(message); - } - } - } // class MdaHelper -#endif // MDA_SUPPORTED - } // class StreamWriter } // namespace diff --git a/src/mscorlib/src/System/IO/StringReader.cs b/src/mscorlib/src/System/IO/StringReader.cs deleted file mode 100644 index e5fa811453..0000000000 --- a/src/mscorlib/src/System/IO/StringReader.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** Purpose: For reading text from strings -** -** -===========================================================*/ - -using System; -using System.Runtime.InteropServices; -using System.Diagnostics.Contracts; -using System.Security.Permissions; -using System.Threading.Tasks; - -namespace System.IO { - // This class implements a text reader that reads from a string. - // - [Serializable] - [ComVisible(true)] - public class StringReader : TextReader - { - private String _s; - private int _pos; - private int _length; - - public StringReader(String s) { - if (s == null) - throw new ArgumentNullException("s"); - Contract.EndContractBlock(); - _s = s; - _length = s == null? 0: s.Length; - } - - // Closes this StringReader. Following a call to this method, the String - // Reader will throw an ObjectDisposedException. - public override void Close() { - Dispose(true); - } - - protected override void Dispose(bool disposing) { - _s = null; - _pos = 0; - _length = 0; - base.Dispose(disposing); - } - - // Returns the next available character without actually reading it from - // the underlying string. The current position of the StringReader is not - // changed by this operation. The returned value is -1 if no further - // characters are available. - // - [Pure] - public override int Peek() { - if (_s == null) - __Error.ReaderClosed(); - if (_pos == _length) return -1; - return _s[_pos]; - } - - // Reads the next character from the underlying string. The returned value - // is -1 if no further characters are available. - // - public override int Read() { - if (_s == null) - __Error.ReaderClosed(); - if (_pos == _length) return -1; - return _s[_pos++]; - } - - // Reads a block of characters. This method will read up to count - // characters from this StringReader into the buffer character - // array starting at position index. Returns the actual number of - // characters read, or zero if the end of the string is reached. - // - public override int Read([In, Out] char[] buffer, int index, int count) { - if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - index < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - if (_s == null) - __Error.ReaderClosed(); - - int n = _length - _pos; - if (n > 0) { - if (n > count) n = count; - _s.CopyTo(_pos, buffer, index, n); - _pos += n; - } - return n; - } - - public override String ReadToEnd() - { - if (_s == null) - __Error.ReaderClosed(); - String s; - if (_pos==0) - s = _s; - else - s = _s.Substring(_pos, _length - _pos); - _pos = _length; - return s; - } - - // Reads a line. A line is defined as a sequence of characters followed by - // a carriage return ('\r'), a line feed ('\n'), or a carriage return - // immediately followed by a line feed. The resulting string does not - // contain the terminating carriage return and/or line feed. The returned - // value is null if the end of the underlying string has been reached. - // - public override String ReadLine() { - if (_s == null) - __Error.ReaderClosed(); - int i = _pos; - while (i < _length) { - char ch = _s[i]; - if (ch == '\r' || ch == '\n') { - String result = _s.Substring(_pos, i - _pos); - _pos = i + 1; - if (ch == '\r' && _pos < _length && _s[_pos] == '\n') _pos++; - return result; - } - i++; - } - if (i > _pos) { - String result = _s.Substring(_pos, i - _pos); - _pos = i; - return result; - } - return null; - } - - #region Task based Async APIs - [ComVisible(false)] - public override Task<String> ReadLineAsync() - { - return Task.FromResult(ReadLine()); - } - - [ComVisible(false)] - public override Task<String> ReadToEndAsync() - { - return Task.FromResult(ReadToEnd()); - } - - [ComVisible(false)] - public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) - { - if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - index < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - - Contract.EndContractBlock(); - - return Task.FromResult(ReadBlock(buffer, index, count)); - } - - [ComVisible(false)] - public override Task<int> ReadAsync(char[] buffer, int index, int count) - { - if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - index < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - return Task.FromResult(Read(buffer, index, count)); - } - #endregion - } -} diff --git a/src/mscorlib/src/System/IO/StringWriter.cs b/src/mscorlib/src/System/IO/StringWriter.cs deleted file mode 100644 index 282a7910c2..0000000000 --- a/src/mscorlib/src/System/IO/StringWriter.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/*============================================================ -** -** -** -** -** Purpose: For writing text to a string -** -** -===========================================================*/ - -using System; -using System.Runtime; -using System.Text; -using System.Globalization; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; -using System.Security.Permissions; -using System.Threading.Tasks; - -namespace System.IO { - // This class implements a text writer that writes to a string buffer and allows - // the resulting sequence of characters to be presented as a string. - // - [Serializable] - [ComVisible(true)] - public class StringWriter : TextWriter - { - private static volatile UnicodeEncoding m_encoding=null; - - private StringBuilder _sb; - private bool _isOpen; - - // Constructs a new StringWriter. A new StringBuilder is automatically - // created and associated with the new StringWriter. - public StringWriter() - : this(new StringBuilder(), CultureInfo.CurrentCulture) - { - } - - public StringWriter(IFormatProvider formatProvider) - : this(new StringBuilder(), formatProvider) { - } - - // Constructs a new StringWriter that writes to the given StringBuilder. - // - public StringWriter(StringBuilder sb) : this(sb, CultureInfo.CurrentCulture) { - } - - public StringWriter(StringBuilder sb, IFormatProvider formatProvider) : base(formatProvider) { - if (sb==null) - throw new ArgumentNullException("sb", Environment.GetResourceString("ArgumentNull_Buffer")); - Contract.EndContractBlock(); - _sb = sb; - _isOpen = true; - } - - public override void Close() - { - Dispose(true); - } - - protected override void Dispose(bool disposing) - { - // Do not destroy _sb, so that we can extract this after we are - // done writing (similar to MemoryStream's GetBuffer & ToArray methods) - _isOpen = false; - base.Dispose(disposing); - } - - - public override Encoding Encoding { - get { - if (m_encoding==null) { - m_encoding = new UnicodeEncoding(false, false); - } - return m_encoding; - } - } - - // Returns the underlying StringBuilder. This is either the StringBuilder - // that was passed to the constructor, or the StringBuilder that was - // automatically created. - // - public virtual StringBuilder GetStringBuilder() { - return _sb; - } - - // Writes a character to the underlying string buffer. - // - public override void Write(char value) { - if (!_isOpen) - __Error.WriterClosed(); - _sb.Append(value); - } - - // Writes a range of a character array to the underlying string buffer. - // This method will write count characters of data into this - // StringWriter from the buffer character array starting at position - // index. - // - public override void Write(char[] buffer, int index, int count) { - if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); - if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); - if (buffer.Length - index < count) - throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); - Contract.EndContractBlock(); - - if (!_isOpen) - __Error.WriterClosed(); - - _sb.Append(buffer, index, count); - } - - // Writes a string to the underlying string buffer. If the given string is - // null, nothing is written. - // - public override void Write(String value) { - if (!_isOpen) - __Error.WriterClosed(); - if (value != null) _sb.Append(value); - } - - - #region Task based Async APIs - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteAsync(char value) - { - Write(value); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteAsync(String value) - { - Write(value); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteAsync(char[] buffer, int index, int count) - { - Write(buffer, index, count); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteLineAsync(char value) - { - WriteLine(value); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteLineAsync(String value) - { - WriteLine(value); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task WriteLineAsync(char[] buffer, int index, int count) - { - WriteLine(buffer, index, count); - return Task.CompletedTask; - } - - [HostProtection(ExternalThreading = true)] - [ComVisible(false)] - public override Task FlushAsync() - { - return Task.CompletedTask; - } - #endregion - - // Returns a string containing the characters written to this TextWriter - // so far. - // - public override String ToString() { - return _sb.ToString(); - } - } -} diff --git a/src/mscorlib/src/System/IO/TextReader.cs b/src/mscorlib/src/System/IO/TextReader.cs index ede482784a..6cbadfbce5 100644 --- a/src/mscorlib/src/System/IO/TextReader.cs +++ b/src/mscorlib/src/System/IO/TextReader.cs @@ -14,11 +14,9 @@ ** ===========================================================*/ -using System; using System.Text; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Reflection; using System.Security.Permissions; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; @@ -35,11 +33,7 @@ namespace System.IO { // There are methods on the Stream class for reading bytes. [Serializable] [ComVisible(true)] -#if FEATURE_REMOTING public abstract class TextReader : MarshalByRefObject, IDisposable { -#else // FEATURE_REMOTING - public abstract class TextReader : IDisposable { -#endif // FEATURE_REMOTING public static readonly TextReader Null = new NullTextReader(); @@ -101,11 +95,11 @@ namespace System.IO { public virtual int Read([In, Out] char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.Ensures(Contract.Result<int>() >= 0); @@ -176,7 +170,6 @@ namespace System.IO { } #region Task based Async APIs - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public virtual Task<String> ReadLineAsync() { @@ -187,7 +180,6 @@ namespace System.IO { this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public async virtual Task<String> ReadToEndAsync() { @@ -201,14 +193,13 @@ namespace System.IO { return sb.ToString(); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public virtual Task<int> ReadAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -232,14 +223,13 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public virtual Task<int> ReadBlockAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); @@ -248,7 +238,6 @@ namespace System.IO { return ReadBlockAsyncInternal(buffer, index, count); } - [HostProtection(ExternalThreading=true)] private async Task<int> ReadBlockAsyncInternal(char[] buffer, int index, int count) { Contract.Requires(buffer != null); @@ -267,11 +256,10 @@ namespace System.IO { } #endregion - [HostProtection(Synchronization=true)] public static TextReader Synchronized(TextReader reader) { if (reader==null) - throw new ArgumentNullException("reader"); + throw new ArgumentNullException(nameof(reader)); Contract.Ensures(Contract.Result<TextReader>() != null); Contract.EndContractBlock(); @@ -383,9 +371,9 @@ namespace System.IO { public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); @@ -399,9 +387,9 @@ namespace System.IO { public override Task<int> ReadAsync(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0 || count < 0) - throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((index < 0 ? nameof(index) : nameof(count)), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); diff --git a/src/mscorlib/src/System/IO/TextWriter.cs b/src/mscorlib/src/System/IO/TextWriter.cs index 165001e1a6..131f69d35d 100644 --- a/src/mscorlib/src/System/IO/TextWriter.cs +++ b/src/mscorlib/src/System/IO/TextWriter.cs @@ -2,24 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -/*============================================================ -** -** -** -** -** -** Purpose: Abstract base class for Text-only Writers. -** Subclasses will include StreamWriter & StringWriter. -** -** -===========================================================*/ - -using System; using System.Text; using System.Threading; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Reflection; using System.Security.Permissions; using System.Globalization; using System.Diagnostics.CodeAnalysis; @@ -35,11 +21,7 @@ namespace System.IO { // There are methods on the Stream class for writing bytes. [Serializable] [ComVisible(true)] -#if FEATURE_REMOTING public abstract class TextWriter : MarshalByRefObject, IDisposable { -#else // FEATURE_REMOTING - public abstract class TextWriter : IDisposable { -#endif // FEATURE_REMOTING public static readonly TextWriter Null = new NullTextWriter(); // This should be initialized to Environment.NewLine, but @@ -128,10 +110,9 @@ namespace System.IO { } - [HostProtection(Synchronization=true)] public static TextWriter Synchronized(TextWriter writer) { if (writer==null) - throw new ArgumentNullException("writer"); + throw new ArgumentNullException(nameof(writer)); Contract.Ensures(Contract.Result<TextWriter>() != null); Contract.EndContractBlock(); @@ -162,11 +143,11 @@ namespace System.IO { // public virtual void Write(char[] buffer, int index, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (index < 0) - throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - index < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); @@ -490,7 +471,6 @@ namespace System.IO { } #region Task based Async APIs - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteAsync(char value) { @@ -503,7 +483,6 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteAsync(String value) { @@ -516,7 +495,6 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task WriteAsync(char[] buffer) { @@ -524,7 +502,6 @@ namespace System.IO { return WriteAsync(buffer, 0, buffer.Length); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteAsync(char[] buffer, int index, int count) { @@ -537,7 +514,6 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteLineAsync(char value) { @@ -550,7 +526,6 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteLineAsync(String value) { @@ -563,7 +538,6 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task WriteLineAsync(char[] buffer) { @@ -571,7 +545,6 @@ namespace System.IO { return WriteLineAsync(buffer, 0, buffer.Length); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteLineAsync(char[] buffer, int index, int count) { @@ -584,14 +557,12 @@ namespace System.IO { tuple, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task WriteLineAsync() { return WriteAsync(CoreNewLine); } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public virtual Task FlushAsync() { diff --git a/src/mscorlib/src/System/IO/UnmanagedMemoryAccessor.cs b/src/mscorlib/src/System/IO/UnmanagedMemoryAccessor.cs index ea70057217..4208ebfb6d 100644 --- a/src/mscorlib/src/System/IO/UnmanagedMemoryAccessor.cs +++ b/src/mscorlib/src/System/IO/UnmanagedMemoryAccessor.cs @@ -19,6 +19,7 @@ using System.Runtime.ConstrainedExecution; using System.Runtime.Versioning; using System.Security.Permissions; using Microsoft.Win32.SafeHandles; +using System.Diagnostics; using System.Diagnostics.Contracts; namespace System.IO { @@ -28,7 +29,6 @@ namespace System.IO { /// this gives better throughput; benchmarks showed about 12-15% better. public class UnmanagedMemoryAccessor : IDisposable { - [System.Security.SecurityCritical] // auto-generated private SafeBuffer _buffer; private Int64 _offset; [ContractPublicPropertyName("Capacity")] @@ -46,35 +46,29 @@ namespace System.IO { // <SecurityKernel Critical="True" Ring="1"> // <ReferencesCritical Name="Method: Initialize(SafeBuffer, Int64, Int64, FileAccess):Void" Ring="1" /> // </SecurityKernel> - [System.Security.SecuritySafeCritical] public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity) { Initialize(buffer, offset, capacity, FileAccess.Read); } - [System.Security.SecuritySafeCritical] // auto-generated public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access) { Initialize(buffer, offset, capacity, access); } - [System.Security.SecuritySafeCritical] // auto-generated -#pragma warning disable 618 - [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] -#pragma warning restore 618 protected void Initialize(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access) { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (capacity < 0) { - throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(capacity), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (buffer.ByteLength < (UInt64)(offset + capacity)) { throw new ArgumentException(Environment.GetResourceString("Argument_OffsetAndCapacityOutOfBounds")); } if (access < FileAccess.Read || access > FileAccess.ReadWrite) { - throw new ArgumentOutOfRangeException("access"); + throw new ArgumentOutOfRangeException(nameof(access)); } Contract.EndContractBlock(); @@ -155,7 +149,6 @@ namespace System.IO { return InternalReadByte(position); } - [System.Security.SecuritySafeCritical] // auto-generated public char ReadChar(Int64 position) { int sizeOfType = sizeof(char); EnsureSafeToRead(position, sizeOfType); @@ -192,7 +185,6 @@ namespace System.IO { } // See comment above. - [System.Security.SecuritySafeCritical] public Int16 ReadInt16(Int64 position) { int sizeOfType = sizeof(Int16); EnsureSafeToRead(position, sizeOfType); @@ -229,7 +221,6 @@ namespace System.IO { } - [System.Security.SecuritySafeCritical] // auto-generated public Int32 ReadInt32(Int64 position) { int sizeOfType = sizeof(Int32); EnsureSafeToRead(position, sizeOfType); @@ -264,7 +255,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated public Int64 ReadInt64(Int64 position) { int sizeOfType = sizeof(Int64); EnsureSafeToRead(position, sizeOfType); @@ -301,18 +291,63 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe Int32 UnsafeReadInt32(byte* pointer) + { + Int32 result; + // check if pointer is aligned + if (((int)pointer & (sizeof(Int32) - 1)) == 0) + { + result = *((Int32*)pointer); + } + else + { + result = (Int32)(*(pointer) | *(pointer + 1) << 8 | *(pointer + 2) << 16 | *(pointer + 3) << 24); + } + + return result; + } public Decimal ReadDecimal(Int64 position) { + const int ScaleMask = 0x00FF0000; + const int SignMask = unchecked((int)0x80000000); + int sizeOfType = sizeof(Decimal); EnsureSafeToRead(position, sizeOfType); - int[] decimalArray = new int[4]; - ReadArray<int>(position, decimalArray, 0, decimalArray.Length); + unsafe + { + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); - return new Decimal(decimalArray); + int lo = UnsafeReadInt32(pointer); + int mid = UnsafeReadInt32(pointer + 4); + int hi = UnsafeReadInt32(pointer + 8); + int flags = UnsafeReadInt32(pointer + 12); + + // Check for invalid Decimal values + if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16))) + { + throw new ArgumentException(Environment.GetResourceString("Arg_BadDecimal")); // Throw same Exception type as Decimal(int[]) ctor for compat + } + + bool isNegative = (flags & SignMask) != 0; + byte scale = (byte)(flags >> 16); + + return new decimal(lo, mid, hi, isNegative, scale); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } } - [System.Security.SecuritySafeCritical] // auto-generated public Single ReadSingle(Int64 position) { int sizeOfType = sizeof(Single); EnsureSafeToRead(position, sizeOfType); @@ -329,7 +364,7 @@ namespace System.IO { // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif - result = *((Single*)(pointer)); + result = BitConverter.Int32BitsToSingle(*((int*)(pointer))); #if ALIGN_ACCESS } else { @@ -348,7 +383,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated public Double ReadDouble(Int64 position) { int sizeOfType = sizeof(Double); EnsureSafeToRead(position, sizeOfType); @@ -365,7 +399,7 @@ namespace System.IO { // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif - result = *((Double*)(pointer)); + result = BitConverter.Int64BitsToDouble(*((long*)(pointer))); #if ALIGN_ACCESS } else { @@ -388,7 +422,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public SByte ReadSByte(Int64 position) { int sizeOfType = sizeof(SByte); @@ -413,7 +446,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public UInt16 ReadUInt16(Int64 position) { int sizeOfType = sizeof(UInt16); @@ -450,7 +482,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public UInt32 ReadUInt32(Int64 position) { int sizeOfType = sizeof(UInt32); @@ -487,7 +518,6 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public UInt64 ReadUInt64(Int64 position) { int sizeOfType = sizeof(UInt64); @@ -540,10 +570,9 @@ namespace System.IO { // such, it is best to use the ReadXXX methods for small standard types such as ints, longs, // bools, etc. - [System.Security.SecurityCritical] // auto-generated_required public void Read<T>(Int64 position, out T structure) where T : struct { if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } Contract.EndContractBlock(); @@ -557,10 +586,10 @@ namespace System.IO { UInt32 sizeOfT = Marshal.SizeOfType(typeof(T)); if (position > _capacity - sizeOfT) { if (position >= _capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } else { - throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToRead", typeof(T).FullName), "position"); + throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToRead", typeof(T).FullName), nameof(position)); } } @@ -573,16 +602,15 @@ namespace System.IO { // struct that contains reference members will most likely cause the runtime to AV. This // is consistent with Marshal.PtrToStructure. - [System.Security.SecurityCritical] // auto-generated_required public int ReadArray<T>(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct { if (array == null) { - throw new ArgumentNullException("array", "Buffer cannot be null."); + throw new ArgumentNullException(nameof(array), "Buffer cannot be null."); } if (offset < 0) { - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (count < 0) { - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (array.Length - offset < count) { throw new ArgumentException(Environment.GetResourceString("Argument_OffsetAndLengthOutOfBounds")); @@ -597,14 +625,14 @@ namespace System.IO { } } if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } UInt32 sizeOfT = Marshal.AlignedSizeOf<T>(); // only check position and ask for fewer Ts if count is too big if (position >= _capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } int n = count; @@ -647,7 +675,6 @@ namespace System.IO { InternalWrite(position, value); } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, char value) { int sizeOfType = sizeof(char); EnsureSafeToWrite(position, sizeOfType); @@ -682,7 +709,6 @@ namespace System.IO { } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, Int16 value) { int sizeOfType = sizeof(Int16); EnsureSafeToWrite(position, sizeOfType); @@ -716,7 +742,6 @@ namespace System.IO { } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, Int32 value) { int sizeOfType = sizeof(Int32); EnsureSafeToWrite(position, sizeOfType); @@ -751,7 +776,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, Int64 value) { int sizeOfType = sizeof(Int64); EnsureSafeToWrite(position, sizeOfType); @@ -789,28 +813,56 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void UnsafeWriteInt32(byte* pointer, Int32 value) + { + // check if pointer is aligned + if (((int)pointer & (sizeof(Int32) - 1)) == 0) + { + *((Int32*)pointer) = value; + } + else + { + *(pointer) = (byte)value; + *(pointer + 1) = (byte)(value >> 8); + *(pointer + 2) = (byte)(value >> 16); + *(pointer + 3) = (byte)(value >> 24); + } + } + public void Write(Int64 position, Decimal value) { int sizeOfType = sizeof(Decimal); EnsureSafeToWrite(position, sizeOfType); - byte[] decimalArray = new byte[16]; - Decimal.GetBytes(value, decimalArray); + unsafe + { + byte* pointer = null; + try + { + _buffer.AcquirePointer(ref pointer); + pointer += (_offset + position); - int[] bits = new int[4]; - int flags = ((int)decimalArray[12]) | ((int)decimalArray[13] << 8) | ((int)decimalArray[14] << 16) | ((int)decimalArray[15] << 24); - int lo = ((int)decimalArray[0]) | ((int)decimalArray[1] << 8) | ((int)decimalArray[2] << 16) | ((int)decimalArray[3] << 24); - int mid = ((int)decimalArray[4]) | ((int)decimalArray[5] << 8) | ((int)decimalArray[6] << 16) | ((int)decimalArray[7] << 24); - int hi = ((int)decimalArray[8]) | ((int)decimalArray[9] << 8) | ((int)decimalArray[10] << 16) | ((int)decimalArray[11] << 24); - bits[0] = lo; - bits[1] = mid; - bits[2] = hi; - bits[3] = flags; + int* valuePtr = (int*)(&value); + int flags = *valuePtr; + int hi = *(valuePtr + 1); + int lo = *(valuePtr + 2); + int mid = *(valuePtr + 3); - WriteArray<int>(position, bits, 0, bits.Length); + UnsafeWriteInt32(pointer, lo); + UnsafeWriteInt32(pointer + 4, mid); + UnsafeWriteInt32(pointer + 8, hi); + UnsafeWriteInt32(pointer + 12, flags); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } + } + } } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, Single value) { int sizeOfType = sizeof(Single); EnsureSafeToWrite(position, sizeOfType); @@ -825,7 +877,7 @@ namespace System.IO { // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif - *((Single*)pointer) = value; + *((int*)pointer) = BitConverter.SingleToInt32Bits(value); #if ALIGN_ACCESS } else { @@ -846,7 +898,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated public void Write(Int64 position, Double value) { int sizeOfType = sizeof(Double); EnsureSafeToWrite(position, sizeOfType); @@ -861,7 +912,7 @@ namespace System.IO { // check if pointer is aligned if (((int)pointer & (sizeOfType - 1)) == 0) { #endif - *((Double*)pointer) = value; + *((long*)pointer) = BitConverter.DoubleToInt64Bits(value); #if ALIGN_ACCESS } else { @@ -886,7 +937,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public void Write(Int64 position, SByte value) { int sizeOfType = sizeof(SByte); @@ -908,7 +958,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public void Write(Int64 position, UInt16 value) { int sizeOfType = sizeof(UInt16); @@ -942,7 +991,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public void Write(Int64 position, UInt32 value) { int sizeOfType = sizeof(UInt32); @@ -979,7 +1027,6 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated [CLSCompliant(false)] public void Write(Int64 position, UInt64 value) { int sizeOfType = sizeof(UInt64); @@ -1024,10 +1071,9 @@ namespace System.IO { // though this is number is JIT and architecture dependent). As such, it is best to use // the WriteX methods for small standard types such as ints, longs, bools, etc. - [System.Security.SecurityCritical] // auto-generated_required public void Write<T>(Int64 position, ref T structure) where T : struct { if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } Contract.EndContractBlock(); @@ -1041,10 +1087,10 @@ namespace System.IO { UInt32 sizeOfT = Marshal.SizeOfType(typeof(T)); if (position > _capacity - sizeOfT) { if (position >= _capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } else { - throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToWrite", typeof(T).FullName), "position"); + throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToWrite", typeof(T).FullName), nameof(position)); } } @@ -1054,25 +1100,24 @@ namespace System.IO { // Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory. - [System.Security.SecurityCritical] // auto-generated_required public void WriteArray<T>(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct { if (array == null) { - throw new ArgumentNullException("array", "Buffer cannot be null."); + throw new ArgumentNullException(nameof(array), "Buffer cannot be null."); } if (offset < 0) { - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (count < 0) { - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (array.Length - offset < count) { throw new ArgumentException(Environment.GetResourceString("Argument_OffsetAndLengthOutOfBounds")); } if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (position >= Capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } Contract.EndContractBlock(); @@ -1086,11 +1131,10 @@ namespace System.IO { _buffer.WriteArray<T>((UInt64)(_offset + position), array, offset, count); } - [System.Security.SecuritySafeCritical] // auto-generated private byte InternalReadByte(Int64 position) { - Contract.Assert(CanRead, "UMA not readable"); - Contract.Assert(position >= 0, "position less than 0"); - Contract.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); + Debug.Assert(CanRead, "UMA not readable"); + Debug.Assert(position >= 0, "position less than 0"); + Debug.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); byte result; unsafe { @@ -1109,11 +1153,10 @@ namespace System.IO { return result; } - [System.Security.SecuritySafeCritical] // auto-generated private void InternalWrite(Int64 position, byte value) { - Contract.Assert(CanWrite, "UMA not writeable"); - Contract.Assert(position >= 0, "position less than 0"); - Contract.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); + Debug.Assert(CanWrite, "UMA not writable"); + Debug.Assert(position >= 0, "position less than 0"); + Debug.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)"); unsafe { byte* pointer = null; @@ -1138,15 +1181,15 @@ namespace System.IO { throw new NotSupportedException(Environment.GetResourceString("NotSupported_Reading")); } if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } Contract.EndContractBlock(); if (position > _capacity - sizeOfType) { if (position >= _capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } else { - throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToRead"), "position"); + throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToRead"), nameof(position)); } } } @@ -1159,15 +1202,15 @@ namespace System.IO { throw new NotSupportedException(Environment.GetResourceString("NotSupported_Writing")); } if (position < 0) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } Contract.EndContractBlock(); if (position > _capacity - sizeOfType) { if (position >= _capacity) { - throw new ArgumentOutOfRangeException("position", Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); + throw new ArgumentOutOfRangeException(nameof(position), Environment.GetResourceString("ArgumentOutOfRange_PositionLessThanCapacityRequired")); } else { - throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToWrite", "Byte"), "position"); + throw new ArgumentException(Environment.GetResourceString("Argument_NotEnoughBytesToWrite", nameof(Byte)), nameof(position)); } } } diff --git a/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs b/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs index 6b506ea5b1..d78632639b 100644 --- a/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs +++ b/src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs @@ -19,6 +19,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Threading; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Threading.Tasks; @@ -43,7 +44,7 @@ namespace System.IO { * * It may become necessary to add in some sort of * DeallocationMode enum, specifying whether we unmap a section of memory, - * call free, run a user-provided delegate to free the memory, etc etc. + * call free, run a user-provided delegate to free the memory, etc. * We'll suggest user write a subclass of UnmanagedMemoryStream that uses * a SafeHandle subclass to hold onto the memory. * Check for problems when using this in the negative parts of a @@ -76,7 +77,7 @@ namespace System.IO { * a. a race condition in WriteX that could have allowed a thread to * read from unzeroed memory was fixed * b. memory region cannot be expanded beyond _capacity; in other - * words, a UMS creator is saying a writeable UMS is safe to + * words, a UMS creator is saying a writable UMS is safe to * write to anywhere in the memory range up to _capacity, specified * in the ctor. Even if the caller doesn't specify a capacity, then * length is used as the capacity. @@ -85,22 +86,19 @@ namespace System.IO { { private const long UnmanagedMemStreamMaxLength = Int64.MaxValue; - [System.Security.SecurityCritical] // auto-generated private SafeBuffer _buffer; - [SecurityCritical] private unsafe byte* _mem; private long _length; private long _capacity; private long _position; private long _offset; private FileAccess _access; - internal bool _isOpen; + internal bool _isOpen; [NonSerialized] private Task<Int32> _lastReadTask; // The last successful task returned from ReadAsync // Needed for subclasses that need to map a file, etc. - [System.Security.SecuritySafeCritical] // auto-generated protected UnmanagedMemoryStream() { unsafe { @@ -109,12 +107,10 @@ namespace System.IO { _isOpen = false; } - [System.Security.SecuritySafeCritical] // auto-generated public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) { Initialize(buffer, offset, length, FileAccess.Read, false); } - [System.Security.SecuritySafeCritical] // auto-generated public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) { Initialize(buffer, offset, length, access, false); } @@ -122,32 +118,29 @@ namespace System.IO { // We must create one of these without doing a security check. This // class is created while security is trying to start up. Plus, doing // a Demand from Assembly.GetManifestResourceStream isn't useful. - [System.Security.SecurityCritical] // auto-generated internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { Initialize(buffer, offset, length, access, skipSecurityCheck); } - [System.Security.SecuritySafeCritical] // auto-generated protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) { Initialize(buffer, offset, length, access, false); } - [System.Security.SecurityCritical] // auto-generated internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) { if (buffer == null) { - throw new ArgumentNullException("buffer"); + throw new ArgumentNullException(nameof(buffer)); } if (offset < 0) { - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (length < 0) { - throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(length), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (buffer.ByteLength < (ulong)(offset + length)) { throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSafeBufferOffLen")); } if (access < FileAccess.Read || access > FileAccess.ReadWrite) { - throw new ArgumentOutOfRangeException("access"); + throw new ArgumentOutOfRangeException(nameof(access)); } Contract.EndContractBlock(); @@ -185,14 +178,12 @@ namespace System.IO { _isOpen = true; } - [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false)] public unsafe UnmanagedMemoryStream(byte* pointer, long length) { Initialize(pointer, length, length, FileAccess.Read, false); } - [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false)] public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) { @@ -202,41 +193,39 @@ namespace System.IO { // We must create one of these without doing a security check. This // class is created while security is trying to start up. Plus, doing // a Demand from Assembly.GetManifestResourceStream isn't useful. - [System.Security.SecurityCritical] // auto-generated internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) { Initialize(pointer, length, capacity, access, skipSecurityCheck); } - [System.Security.SecurityCritical] // auto-generated [CLSCompliant(false)] protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) { Initialize(pointer, length, capacity, access, false); } - [System.Security.SecurityCritical] // auto-generated internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) { if (pointer == null) - throw new ArgumentNullException("pointer"); + throw new ArgumentNullException(nameof(pointer)); if (length < 0 || capacity < 0) - throw new ArgumentOutOfRangeException((length < 0) ? "length" : "capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException((length < 0) ? nameof(length) : nameof(capacity), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (length > capacity) - throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity")); + throw new ArgumentOutOfRangeException(nameof(length), Environment.GetResourceString("ArgumentOutOfRange_LengthGreaterThanCapacity")); Contract.EndContractBlock(); // Check for wraparound. if (((byte*) ((long)pointer + capacity)) < pointer) - throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround")); + throw new ArgumentOutOfRangeException(nameof(capacity), Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround")); if (access < FileAccess.Read || access > FileAccess.ReadWrite) - throw new ArgumentOutOfRangeException("access", Environment.GetResourceString("ArgumentOutOfRange_Enum")); + throw new ArgumentOutOfRangeException(nameof(access), Environment.GetResourceString("ArgumentOutOfRange_Enum")); if (_isOpen) throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_CalledTwice")); - if (!skipSecurityCheck) + if (!skipSecurityCheck) { #pragma warning disable 618 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); #pragma warning restore 618 + } _mem = pointer; _offset = 0; @@ -261,7 +250,6 @@ namespace System.IO { get { return _isOpen && (_access & FileAccess.Write) != 0; } } - [System.Security.SecuritySafeCritical] // auto-generated protected override void Dispose(bool disposing) { _isOpen = false; @@ -277,7 +265,6 @@ namespace System.IO { if (!_isOpen) __Error.StreamIsClosed(); } - [HostProtection(ExternalThreading=true)] [ComVisible(false)] public override Task FlushAsync(CancellationToken cancellationToken) { @@ -316,10 +303,9 @@ namespace System.IO { Contract.EndContractBlock(); return Interlocked.Read(ref _position); } - [System.Security.SecuritySafeCritical] // auto-generated set { if (value < 0) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); Contract.EndContractBlock(); if (!CanSeek) __Error.StreamIsClosed(); @@ -327,7 +313,7 @@ namespace System.IO { unsafe { // On 32 bit machines, ensure we don't wrap around. if (value > (long) Int32.MaxValue || _mem + value < _mem) - throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); + throw new ArgumentOutOfRangeException(nameof(value), Environment.GetResourceString("ArgumentOutOfRange_StreamLength")); } #endif Interlocked.Exchange(ref _position, value); @@ -336,7 +322,6 @@ namespace System.IO { [CLSCompliant(false)] public unsafe byte* PositionPointer { - [System.Security.SecurityCritical] // auto-generated_required get { if (_buffer != null) { throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); @@ -350,7 +335,6 @@ namespace System.IO { if (!_isOpen) __Error.StreamIsClosed(); return ptr; } - [System.Security.SecurityCritical] // auto-generated_required set { if (_buffer != null) throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); @@ -368,7 +352,6 @@ namespace System.IO { } internal unsafe byte* Pointer { - [System.Security.SecurityCritical] // auto-generated get { if (_buffer != null) throw new NotSupportedException(Environment.GetResourceString("NotSupported_UmsSafeBuffer")); @@ -377,14 +360,13 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated public override int Read([In, Out] byte[] buffer, int offset, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync @@ -404,42 +386,49 @@ namespace System.IO { int nInt = (int) n; // Safe because n <= count, which is an Int32 if (nInt < 0) - nInt = 0; // _position could be beyond EOF - Contract.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. - - if (_buffer != null) { - unsafe { - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(buffer, offset, pointer + pos + _offset, 0, nInt); - } - finally { - if (pointer != null) { - _buffer.ReleasePointer(); + return 0; // _position could be beyond EOF + Debug.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1. + + unsafe + { + fixed (byte* pBuffer = buffer) + { + if (_buffer != null) + { + byte* pointer = null; + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pBuffer + offset, pointer + pos + _offset, nInt); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } } } - } - } - else { - unsafe { - Buffer.Memcpy(buffer, offset, _mem + pos, 0, nInt); + else + { + Buffer.Memcpy(pBuffer + offset, _mem + pos, nInt); + } } } Interlocked.Exchange(ref _position, pos + n); return nInt; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task<Int32> ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // contract validation copied from Read(...) @@ -455,12 +444,11 @@ namespace System.IO { } catch (Exception ex) { - Contract.Assert(! (ex is OperationCanceledException)); + Debug.Assert(! (ex is OperationCanceledException)); return Task.FromException<Int32>(ex); } } - [System.Security.SecuritySafeCritical] // auto-generated public override int ReadByte() { if (!_isOpen) __Error.StreamIsClosed(); if (!CanRead) __Error.ReadNotSupported(); @@ -497,7 +485,7 @@ namespace System.IO { public override long Seek(long offset, SeekOrigin loc) { if (!_isOpen) __Error.StreamIsClosed(); if (offset > UnmanagedMemStreamMaxLength) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_UnmanagedMemStreamLength")); switch(loc) { case SeekOrigin.Begin: if (offset < 0) @@ -524,11 +512,10 @@ namespace System.IO { } long finalPos = Interlocked.Read(ref _position); - Contract.Assert(finalPos >= 0, "_position >= 0"); + Debug.Assert(finalPos >= 0, "_position >= 0"); return finalPos; } - [System.Security.SecuritySafeCritical] // auto-generated public override void SetLength(long value) { if (value < 0) throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); @@ -554,14 +541,13 @@ namespace System.IO { } } - [System.Security.SecuritySafeCritical] // auto-generated public override void Write(byte[] buffer, int offset, int count) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..) @@ -595,46 +581,52 @@ namespace System.IO { } } - if (_buffer != null) { - - long bytesLeft = _capacity - pos; - if (bytesLeft < count) { - throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); - } + unsafe + { + fixed (byte* pBuffer = buffer) + { + if (_buffer != null) + { + long bytesLeft = _capacity - pos; + if (bytesLeft < count) + { + throw new ArgumentException(Environment.GetResourceString("Arg_BufferTooSmall")); + } - unsafe { - byte* pointer = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try { - _buffer.AcquirePointer(ref pointer); - Buffer.Memcpy(pointer + pos + _offset, 0, buffer, offset, count); - } - finally { - if (pointer != null) { - _buffer.ReleasePointer(); + byte* pointer = null; + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + _buffer.AcquirePointer(ref pointer); + Buffer.Memcpy(pointer + pos + _offset, pBuffer + offset, count); + } + finally + { + if (pointer != null) + { + _buffer.ReleasePointer(); + } } } - } - } - else { - unsafe { - Buffer.Memcpy(_mem + pos, 0, buffer, offset, count); + else + { + Buffer.Memcpy(_mem + pos, pBuffer + offset, count); + } } } Interlocked.Exchange(ref _position, n); return; } - [HostProtection(ExternalThreading = true)] [ComVisible(false)] public override Task WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) { if (buffer==null) - throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); + throw new ArgumentNullException(nameof(buffer), Environment.GetResourceString("ArgumentNull_Buffer")); if (offset < 0) - throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(offset), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (count < 0) - throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(count), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); if (buffer.Length - offset < count) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); Contract.EndContractBlock(); // contract validation copied from Write(..) @@ -649,13 +641,12 @@ namespace System.IO { } catch (Exception ex) { - Contract.Assert(! (ex is OperationCanceledException)); + Debug.Assert(! (ex is OperationCanceledException)); return Task.FromException<Int32>(ex); } } - [System.Security.SecuritySafeCritical] // auto-generated public override void WriteByte(byte value) { if (!_isOpen) __Error.StreamIsClosed(); if (!CanWrite) __Error.WriteNotSupported(); @@ -674,7 +665,7 @@ namespace System.IO { // Check to see whether we are now expanding the stream and must // zero any memory in the middle. // don't do if created from SafeBuffer - if (_buffer == null) { + if (_buffer == null) { if (pos > len) { unsafe { Buffer.ZeroMemory(_mem+len, pos-len); diff --git a/src/mscorlib/src/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/mscorlib/src/System/IO/UnmanagedMemoryStreamWrapper.cs index 5727eeddf5..040ddbb3d7 100644 --- a/src/mscorlib/src/System/IO/UnmanagedMemoryStreamWrapper.cs +++ b/src/mscorlib/src/System/IO/UnmanagedMemoryStreamWrapper.cs @@ -106,7 +106,6 @@ namespace System.IO { return _unmanagedStream.Seek(offset, loc); } - [System.Security.SecuritySafeCritical] // auto-generated public unsafe override byte[] ToArray() { if (!_unmanagedStream._isOpen) __Error.StreamIsClosed(); if (!_unmanagedStream.CanRead) __Error.ReadNotSupported(); @@ -127,7 +126,7 @@ namespace System.IO { // Writes this MemoryStream to another stream. public unsafe override void WriteTo(Stream stream) { if (stream==null) - throw new ArgumentNullException("stream", Environment.GetResourceString("ArgumentNull_Stream")); + throw new ArgumentNullException(nameof(stream), Environment.GetResourceString("ArgumentNull_Stream")); Contract.EndContractBlock(); if (!_unmanagedStream._isOpen) __Error.StreamIsClosed(); @@ -151,16 +150,16 @@ namespace System.IO { // The parameter checks must be in sync with the base version: if (destination == null) - throw new ArgumentNullException("destination"); + throw new ArgumentNullException(nameof(destination)); if (bufferSize <= 0) - throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); + throw new ArgumentOutOfRangeException(nameof(bufferSize), Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum")); if (!CanRead && !CanWrite) throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed")); if (!destination.CanRead && !destination.CanWrite) - throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed")); + throw new ObjectDisposedException(nameof(destination), Environment.GetResourceString("ObjectDisposed_StreamClosed")); if (!CanRead) throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream")); diff --git a/src/mscorlib/src/System/IO/__DebugOutputTextWriter.cs b/src/mscorlib/src/System/IO/__DebugOutputTextWriter.cs deleted file mode 100644 index 621bc41b4b..0000000000 --- a/src/mscorlib/src/System/IO/__DebugOutputTextWriter.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if _DEBUG -// This class writes to wherever OutputDebugString writes to. If you don't have -// a Windows app (ie, something hosted in IE), you can use this to redirect Console -// output for some good old-fashioned console spew in MSDEV's debug output window. - -using System; -using System.IO; -using System.Text; -using System.Security; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using Microsoft.Win32; -using System.Globalization; - -namespace System.IO { - internal class __DebugOutputTextWriter : TextWriter { - private readonly String _consoleType; - - internal __DebugOutputTextWriter(String consoleType): base(CultureInfo.InvariantCulture) - { - _consoleType = consoleType; - } - - public override Encoding Encoding { -#if FEATURE_CORECLR - [System.Security.SecuritySafeCritical] -#endif - get { - if (Marshal.SystemDefaultCharSize == 1) - return Encoding.Default; - else - return new UnicodeEncoding(false, false); - } - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void Write(char c) - { - OutputDebugString(c.ToString()); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void Write(String str) - { - OutputDebugString(str); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void Write(char[] array) - { - if (array != null) - OutputDebugString(new String(array)); - } - - [System.Security.SecuritySafeCritical] // auto-generated - public override void WriteLine(String str) - { - if (str != null) - OutputDebugString(_consoleType + str); - else - OutputDebugString("<null>"); - OutputDebugString(new String(CoreNewLine)); - } - - [System.Security.SecurityCritical] // auto-generated - [DllImport(Win32Native.KERNEL32, CharSet=CharSet.Auto)] - [SuppressUnmanagedCodeSecurityAttribute()] - private static extern void OutputDebugString(String output); - } -} - -#endif // _DEBUG diff --git a/src/mscorlib/src/System/IO/__Error.cs b/src/mscorlib/src/System/IO/__Error.cs index a31d9657e8..ad4972de54 100644 --- a/src/mscorlib/src/System/IO/__Error.cs +++ b/src/mscorlib/src/System/IO/__Error.cs @@ -74,7 +74,6 @@ namespace System.IO { // discovery permission to that path. If we do not, return just the // file name. If we know it is a directory, then don't return the // directory name. - [System.Security.SecurityCritical] // auto-generated internal static String GetDisplayablePath(String path, bool isInvalidPath) { @@ -85,7 +84,7 @@ namespace System.IO { bool isFullyQualified = false; if (path.Length < 2) return path; - if (Path.IsDirectorySeparator(path[0]) && Path.IsDirectorySeparator(path[1])) + if (PathInternal.IsDirectorySeparator(path[0]) && PathInternal.IsDirectorySeparator(path[1])) isFullyQualified = true; else if (path[1] == Path.VolumeSeparatorChar) { isFullyQualified = true; @@ -97,9 +96,6 @@ namespace System.IO { bool safeToReturn = false; try { if (!isInvalidPath) { -#if !FEATURE_CORECLR - new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new String[] { path }, false, false).Demand(); -#endif safeToReturn = true; } } @@ -116,7 +112,7 @@ namespace System.IO { } if (!safeToReturn) { - if (Path.IsDirectorySeparator(path[path.Length - 1])) + if (PathInternal.IsDirectorySeparator(path[path.Length - 1])) path = Environment.GetResourceString("IO.IO_NoPermissionToDirectoryName"); else path = Path.GetFileName(path); @@ -125,7 +121,6 @@ namespace System.IO { return path; } - [System.Security.SecuritySafeCritical] // auto-generated internal static void WinIOError() { int errorCode = Marshal.GetLastWin32Error(); WinIOError(errorCode, String.Empty); @@ -136,7 +131,6 @@ namespace System.IO { // will determine the appropriate exception to throw dependent on your // error, and depending on the error, insert a string into the message // gotten from the ResourceManager. - [System.Security.SecurityCritical] // auto-generated internal static void WinIOError(int errorCode, String maybeFullPath) { // This doesn't have to be perfect, but is a perf optimization. bool isInvalidPath = errorCode == Win32Native.ERROR_INVALID_NAME || errorCode == Win32Native.ERROR_BAD_PATHNAME; @@ -195,13 +189,11 @@ namespace System.IO { } // An alternative to WinIOError with friendlier messages for drives - [System.Security.SecuritySafeCritical] // auto-generated internal static void WinIODriveError(String driveName) { int errorCode = Marshal.GetLastWin32Error(); WinIODriveError(driveName, errorCode); } - [System.Security.SecurityCritical] // auto-generated internal static void WinIODriveError(String driveName, int errorCode) { switch (errorCode) { |