summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/IO
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2016-12-27 07:46:08 (GMT)
committerJiyoung Yun <jy910.yun@samsung.com>2016-12-27 07:46:08 (GMT)
commitdb20f3f1bb8595633a7e16c8900fd401a453a6b5 (patch)
treee5435159cd1bf0519276363a6fe1663d1721bed3 /src/mscorlib/src/System/IO
parent4b4aad7217d3292650e77eec2cf4c198ea9c3b4b (diff)
downloadcoreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.zip
coreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.tar.gz
coreclr-db20f3f1bb8595633a7e16c8900fd401a453a6b5.tar.bz2
Imported Upstream version 1.0.0.9127upstream/1.0.0.9127
Diffstat (limited to 'src/mscorlib/src/System/IO')
-rw-r--r--src/mscorlib/src/System/IO/BinaryReader.cs46
-rw-r--r--src/mscorlib/src/System/IO/BinaryWriter.cs37
-rw-r--r--src/mscorlib/src/System/IO/BufferedStream.cs1320
-rw-r--r--src/mscorlib/src/System/IO/Directory.cs642
-rw-r--r--src/mscorlib/src/System/IO/DirectoryInfo.cs265
-rw-r--r--src/mscorlib/src/System/IO/DriveInfo.cs281
-rw-r--r--src/mscorlib/src/System/IO/EncodingCache.cs13
-rw-r--r--src/mscorlib/src/System/IO/File.cs658
-rw-r--r--src/mscorlib/src/System/IO/FileAttributes.cs20
-rw-r--r--src/mscorlib/src/System/IO/FileInfo.cs207
-rw-r--r--src/mscorlib/src/System/IO/FileLoadException.cs14
-rw-r--r--src/mscorlib/src/System/IO/FileNotFoundException.cs11
-rw-r--r--src/mscorlib/src/System/IO/FileSecurityState.cs133
-rw-r--r--src/mscorlib/src/System/IO/FileSecurityStateAccess.cs32
-rw-r--r--src/mscorlib/src/System/IO/FileStream.cs2695
-rw-r--r--src/mscorlib/src/System/IO/FileSystemEnumerable.cs185
-rw-r--r--src/mscorlib/src/System/IO/FileSystemInfo.cs137
-rw-r--r--src/mscorlib/src/System/IO/LongPathHelper.cs521
-rw-r--r--src/mscorlib/src/System/IO/MemoryStream.cs112
-rw-r--r--src/mscorlib/src/System/IO/Path.cs1435
-rw-r--r--src/mscorlib/src/System/IO/PathHelper.cs448
-rw-r--r--src/mscorlib/src/System/IO/PathInternal.cs806
-rw-r--r--src/mscorlib/src/System/IO/PinnedBufferMemoryStream.cs6
-rw-r--r--src/mscorlib/src/System/IO/Stream.cs144
-rw-r--r--src/mscorlib/src/System/IO/StreamHelpers.CopyValidation.cs46
-rw-r--r--src/mscorlib/src/System/IO/StreamReader.cs100
-rw-r--r--src/mscorlib/src/System/IO/StreamWriter.cs175
-rw-r--r--src/mscorlib/src/System/IO/StringReader.cs187
-rw-r--r--src/mscorlib/src/System/IO/StringWriter.cs196
-rw-r--r--src/mscorlib/src/System/IO/TextReader.cs36
-rw-r--r--src/mscorlib/src/System/IO/TextWriter.cs37
-rw-r--r--src/mscorlib/src/System/IO/UnmanagedMemoryAccessor.cs215
-rw-r--r--src/mscorlib/src/System/IO/UnmanagedMemoryStream.cs187
-rw-r--r--src/mscorlib/src/System/IO/UnmanagedMemoryStreamWrapper.cs9
-rw-r--r--src/mscorlib/src/System/IO/__DebugOutputTextWriter.cs76
-rw-r--r--src/mscorlib/src/System/IO/__Error.cs12
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 8accf0b..4145a7f 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 c775cbc..f99b4d3 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 0c73b5c..0000000
--- 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 be74538..d6b6822 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 f7b0709..c4c350d 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 be75e89..0000000
--- 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 0000000..53379bc
--- /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 cfcb469..9f89f81 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 19d5f22..51ef597 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 3ab1a51..32622c6 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 fabe261..2b56c00 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 933e4fd..8cc75f8 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 249848a..0000000
--- 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 b6378c6..0000000
--- 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 deef30c..0000000
--- 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 c2e603c..f861805 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 7a17a41..94cd531 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 9746fdc..0000000
--- 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 edb583b..bdddc83 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 4f79936..0000000
--- 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 8e39b3c..0000000
--- 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 3970e22..0000000
--- 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 (", &lt;, &gt;, |
- /// 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 4fd54f5..890669f 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 a1f2936..3cdfad6 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 0000000..8ff0e04
--- /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 549733b..708db08 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 65613bb..22eba82 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 e5fa811..0000000
--- 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 282a791..0000000
--- 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 ede4827..6cbadfb 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 165001e..131f69d 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 ea70057..4208ebf 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) {