X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.IO%2FFileStream.cs;h=7eae1656a62fee71e4ac6ef48b3f23ac2057e123;hb=2d23bfcbce7a3f7e54dcd5911adb88b244baca35;hp=0c504f56ee09e68d4e21e771e3f17f89288aa956;hpb=f84f760a1c8a8c0ec6ae16b7f38d14a49d329ad7;p=mono.git diff --git a/mcs/class/corlib/System.IO/FileStream.cs b/mcs/class/corlib/System.IO/FileStream.cs index 0c504f56ee0..7eae1656a62 100644 --- a/mcs/class/corlib/System.IO/FileStream.cs +++ b/mcs/class/corlib/System.IO/FileStream.cs @@ -1,13 +1,14 @@ // -// System.IO/FileStream.cs +// System.IO.FileStream.cs // // Authors: // Dietmar Maurer (dietmar@ximian.com) // Dan Lewis (dihlewis@yahoo.co.uk) // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Marek Safar (marek.safar@gmail.com) // // (C) 2001-2003 Ximian, Inc. http://www.ximian.com -// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// Copyright (C) 2004-2005, 2008, 2010 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -34,33 +35,42 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Remoting.Messaging; +using System.Security; using System.Security.Permissions; using System.Threading; -#if NET_2_0 using Microsoft.Win32.SafeHandles; +#if NET_2_1 +using System.IO.IsolatedStorage; +#else +using System.Security.AccessControl; #endif namespace System.IO { + [ComVisible (true)] public class FileStream : Stream { // construct from handle + [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")] public FileStream (IntPtr handle, FileAccess access) : this (handle, access, true, DefaultBufferSize, false) {} + [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")] public FileStream (IntPtr handle, FileAccess access, bool ownsHandle) : this (handle, access, ownsHandle, DefaultBufferSize, false) {} + [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")] public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize) : this (handle, access, ownsHandle, bufferSize, false) {} + [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")] public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync) : this (handle, access, ownsHandle, bufferSize, isAsync, false) {} [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)] - internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool noBuffering) + internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isZeroSize) { this.handle = MonoIO.InvalidHandle; if (handle == this.handle) @@ -82,17 +92,23 @@ namespace System.IO this.canseek = true; } else { this.canseek = false; - noBuffering = true; - bufferSize = 0; } this.handle = handle; this.access = access; this.owner = ownsHandle; this.async = isAsync; +#if MOONLIGHT + // default the browser to 'all' anonymous files and let other usage (like smcs) with 'normal' + // (i.e. non-anonymous except for isolated storage) files and paths + this.anonymous = SecurityManager.SecurityEnabled; +#else this.anonymous = false; +#endif + if (isZeroSize) + bufferSize = 1; - InitBuffer (bufferSize, noBuffering); + InitBuffer (bufferSize); if (canseek) { buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error); @@ -107,80 +123,130 @@ namespace System.IO // construct from filename - public FileStream (string name, FileMode mode) - : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None) + public FileStream (string path, FileMode mode) + : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None) { } - public FileStream (string name, FileMode mode, FileAccess access) - : this (name, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false) + public FileStream (string path, FileMode mode, FileAccess access) + : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false) { } - public FileStream (string name, FileMode mode, FileAccess access, FileShare share) - : this (name, mode, access, share, DefaultBufferSize, false, FileOptions.None) + public FileStream (string path, FileMode mode, FileAccess access, FileShare share) + : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None) { } - - public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize) - : this (name, mode, access, share, bufferSize, false, FileOptions.None) + + public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) + : this (path, mode, access, share, bufferSize, false, FileOptions.None) + { + } + + public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) + : this (path, mode, access, share, bufferSize, useAsync, FileOptions.None) { } - public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync) - : this (name, mode, access, share, bufferSize, isAsync, FileOptions.None) +#if !NET_2_1 + public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + : this (path, mode, access, share, bufferSize, false, options) + { + } + + public FileStream (SafeFileHandle handle, FileAccess access) + :this(handle, access, DefaultBufferSize, false) + { + } + + public FileStream (SafeFileHandle handle, FileAccess access, + int bufferSize) + :this(handle, access, bufferSize, false) + { + } + + [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")] + public FileStream (SafeFileHandle handle, FileAccess access, + int bufferSize, bool isAsync) + :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync) { + this.safeHandle = handle; } -#if NET_2_0 - public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - : this (name, mode, access, share, bufferSize, false, options) + [MonoLimitation ("This ignores the rights parameter")] + public FileStream (string path, FileMode mode, + FileSystemRights rights, FileShare share, + int bufferSize, FileOptions options) + : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options) + { + } + + [MonoLimitation ("This ignores the rights and fileSecurity parameters")] + public FileStream (string path, FileMode mode, + FileSystemRights rights, FileShare share, + int bufferSize, FileOptions options, + FileSecurity fileSecurity) + : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options) { } #endif - internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous) - : this (name, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None) + internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous) + : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None) { } - internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options) + internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options) { - if (name == null) { - throw new ArgumentNullException ("name"); + if (path == null) { + throw new ArgumentNullException ("path"); } - - if (name.Length == 0) { - throw new ArgumentException ("Name is empty"); + + if (path.Length == 0) { + throw new ArgumentException ("Path is empty"); } -#if NET_2_0 // ignore the Inheritable flag share &= ~FileShare.Inheritable; -#endif - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException ("Positive number required."); + throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required."); - if (mode < FileMode.CreateNew || mode > FileMode.Append) + if (mode < FileMode.CreateNew || mode > FileMode.Append) { +#if NET_2_1 + if (anonymous) + throw new ArgumentException ("mode", "Enum value was out of legal range."); + else +#endif throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range."); + } - if (access < FileAccess.Read || access > FileAccess.ReadWrite) + if (access < FileAccess.Read || access > FileAccess.ReadWrite) { +#if NET_2_1 + if (anonymous) + throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range."); + else +#endif throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range."); + } - if (share < FileShare.None || share > FileShare.ReadWrite) + if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) { +#if NET_2_1 + if (anonymous) + throw new IsolatedStorageException ("Enum value for FileShare was out of legal range."); + else +#endif throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range."); + } - if (name.IndexOfAny (Path.InvalidPathChars) != -1) { + if (path.IndexOfAny (Path.InvalidPathChars) != -1) { throw new ArgumentException ("Name has invalid chars"); } - if (Directory.Exists (name)) { + if (Directory.Exists (path)) { // don't leak the path information for isolated storage string msg = Locale.GetText ("Access to the path '{0}' is denied."); - string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name); - throw new UnauthorizedAccessException (String.Format (msg, fname)); + throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false))); } /* Append streams can't be read (see FileMode @@ -198,38 +264,48 @@ namespace System.IO throw new ArgumentException (string.Format (msg, access, mode)); } - string dname = Path.GetDirectoryName (name); + SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight + + string dname = Path.GetDirectoryName (path); if (dname.Length > 0) { string fp = Path.GetFullPath (dname); if (!Directory.Exists (fp)) { // don't leak the path information for isolated storage string msg = Locale.GetText ("Could not find a part of the path \"{0}\"."); - string fname = (anonymous) ? dname : fp; + string fname = (anonymous) ? dname : Path.GetFullPath (path); +#if NET_2_1 + // don't use GetSecureFileName for the directory name + throw new IsolatedStorageException (String.Format (msg, fname)); +#else throw new DirectoryNotFoundException (String.Format (msg, fname)); +#endif } } if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate && - mode != FileMode.CreateNew && !File.Exists (name)) { + mode != FileMode.CreateNew && !File.Exists (path)) { // don't leak the path information for isolated storage string msg = Locale.GetText ("Could not find file \"{0}\"."); - string fname = (anonymous) ? Path.GetFileName (name) : name; + string fname = GetSecureFileName (path); +#if NET_2_1 + throw new IsolatedStorageException (String.Format (msg, fname)); +#else throw new FileNotFoundException (String.Format (msg, fname), fname); +#endif } // IsolatedStorage needs to keep the Name property to the default "[Unknown]" if (!anonymous) - this.name = name; + this.name = path; // TODO: demand permissions MonoIOError error; - this.handle = MonoIO.Open (name, mode, access, share, options, out error); + this.handle = MonoIO.Open (path, mode, access, share, options, out error); if (handle == MonoIO.InvalidHandle) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (path), error); } this.access = access; @@ -255,7 +331,7 @@ namespace System.IO } } - InitBuffer (bufferSize, false); + InitBuffer (bufferSize); if (mode==FileMode.Append) { this.Seek (0, SeekOrigin.End); @@ -274,18 +350,18 @@ namespace System.IO } } - public override bool CanWrite { - get { + public override bool CanWrite { + get { return access == FileAccess.Write || - access == FileAccess.ReadWrite; - } - } + access == FileAccess.ReadWrite; + } + } public override bool CanSeek { - get { - return(canseek); - } - } + get { + return(canseek); + } + } public virtual bool IsAsync { get { @@ -295,7 +371,11 @@ namespace System.IO public string Name { get { - return name; +#if MOONLIGHT + return SecurityManager.CheckElevatedPermissions () ? name : "[Unknown]"; +#else + return name; +#endif } } @@ -316,8 +396,7 @@ namespace System.IO length = MonoIO.GetLength (handle, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } return(length); @@ -350,6 +429,7 @@ namespace System.IO } } + [Obsolete ("Use SafeFileHandle instead")] public virtual IntPtr Handle { [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)] @@ -358,13 +438,21 @@ namespace System.IO } } -#if NET_2_0 public virtual SafeFileHandle SafeFileHandle { [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)] - get { throw new NotImplementedException (); } + get { + SafeFileHandle ret; + + if (safeHandle != null) + ret = safeHandle; + else + ret = new SafeFileHandle (handle, owner); + + FlushBuffer (); + return ret; + } } -#endif // methods @@ -417,38 +505,38 @@ namespace System.IO buf_dirty = true; } - public override int Read ([In,Out] byte[] dest, int dest_offset, int count) + public override int Read ([In,Out] byte[] array, int offset, int count) { if (handle == MonoIO.InvalidHandle) throw new ObjectDisposedException ("Stream has been closed"); - if (dest == null) - throw new ArgumentNullException ("destFile"); + if (array == null) + throw new ArgumentNullException ("array"); if (!CanRead) throw new NotSupportedException ("Stream does not support reading"); - int len = dest.Length; - if (dest_offset < 0) - throw new ArgumentOutOfRangeException ("dest_offset", "< 0"); + int len = array.Length; + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset", "< 0"); if (count < 0) throw new ArgumentOutOfRangeException ("count", "< 0"); - if (dest_offset > len) + if (offset > len) throw new ArgumentException ("destination offset is beyond array size"); // reordered to avoid possible integer overflow - if (dest_offset > len - count) + if (offset > len - count) throw new ArgumentException ("Reading would overrun buffer"); if (async) { - IAsyncResult ares = BeginRead (dest, dest_offset, count, null, null); + IAsyncResult ares = BeginRead (array, offset, count, null, null); return EndRead (ares); } - return ReadInternal (dest, dest_offset, count); + return ReadInternal (array, offset, count); } - int ReadInternal (byte [] dest, int dest_offset, int count) + int ReadInternal (byte [] dest, int offset, int count) { int copied = 0; - int n = ReadSegment (dest, dest_offset, count); + int n = ReadSegment (dest, offset, count); copied += n; count -= n; @@ -466,7 +554,7 @@ namespace System.IO */ FlushBuffer(); n = ReadData (handle, dest, - dest_offset+copied, + offset+copied, count); /* Make the next buffer read @@ -476,7 +564,7 @@ namespace System.IO } else { RefillBuffer (); n = ReadSegment (dest, - dest_offset+copied, + offset+copied, count); } @@ -487,8 +575,8 @@ namespace System.IO delegate int ReadDelegate (byte [] buffer, int offset, int count); - public override IAsyncResult BeginRead (byte [] buffer, int offset, int count, - AsyncCallback cback, object state) + public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes, + AsyncCallback userCallback, object stateObject) { if (handle == MonoIO.InvalidHandle) throw new ObjectDisposedException ("Stream has been closed"); @@ -496,92 +584,94 @@ namespace System.IO if (!CanRead) throw new NotSupportedException ("This stream does not support reading"); - if (buffer == null) - throw new ArgumentNullException ("buffer"); + if (array == null) + throw new ArgumentNullException ("array"); - if (count < 0) - throw new ArgumentOutOfRangeException ("count", "Must be >= 0"); + if (numBytes < 0) + throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0"); if (offset < 0) throw new ArgumentOutOfRangeException ("offset", "Must be >= 0"); // reordered to avoid possible integer overflow - if (count > buffer.Length - offset) - throw new ArgumentException ("Buffer too small. count/offset wrong."); + if (numBytes > array.Length - offset) + throw new ArgumentException ("Buffer too small. numBytes/offset wrong."); if (!async) - return base.BeginRead (buffer, offset, count, cback, state); + return base.BeginRead (array, offset, numBytes, userCallback, stateObject); ReadDelegate r = new ReadDelegate (ReadInternal); - return r.BeginInvoke (buffer, offset, count, cback, state); + return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject); } - public override int EndRead (IAsyncResult async_result) + public override int EndRead (IAsyncResult asyncResult) { - if (async_result == null) - throw new ArgumentNullException ("async_result"); + if (asyncResult == null) + throw new ArgumentNullException ("asyncResult"); if (!async) - return base.EndRead (async_result); + return base.EndRead (asyncResult); - AsyncResult ares = async_result as AsyncResult; + AsyncResult ares = asyncResult as AsyncResult; if (ares == null) - throw new ArgumentException ("Invalid IAsyncResult", "async_result"); + throw new ArgumentException ("Invalid IAsyncResult", "asyncResult"); ReadDelegate r = ares.AsyncDelegate as ReadDelegate; if (r == null) - throw new ArgumentException ("Invalid IAsyncResult", "async_result"); + throw new ArgumentException ("Invalid IAsyncResult", "asyncResult"); - return r.EndInvoke (async_result); + return r.EndInvoke (asyncResult); } - public override void Write (byte[] src, int src_offset, int count) + public override void Write (byte[] array, int offset, int count) { if (handle == MonoIO.InvalidHandle) throw new ObjectDisposedException ("Stream has been closed"); - if (src == null) - throw new ArgumentNullException ("src"); - if (src_offset < 0) - throw new ArgumentOutOfRangeException ("src_offset", "< 0"); + if (array == null) + throw new ArgumentNullException ("array"); + if (offset < 0) + throw new ArgumentOutOfRangeException ("offset", "< 0"); if (count < 0) throw new ArgumentOutOfRangeException ("count", "< 0"); // ordered to avoid possible integer overflow - if (src_offset > src.Length - count) + if (offset > array.Length - count) throw new ArgumentException ("Reading would overrun buffer"); if (!CanWrite) throw new NotSupportedException ("Stream does not support writing"); if (async) { - IAsyncResult ares = BeginWrite (src, src_offset, count, null, null); + IAsyncResult ares = BeginWrite (array, offset, count, null, null); EndWrite (ares); return; } - WriteInternal (src, src_offset, count); + WriteInternal (array, offset, count); } - void WriteInternal (byte [] src, int src_offset, int count) + void WriteInternal (byte [] src, int offset, int count) { if (count > buf_size) { // shortcut for long writes MonoIOError error; FlushBuffer (); - - MonoIO.Write (handle, src, src_offset, count, out error); - if (error != MonoIOError.ERROR_SUCCESS) { - // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); - } + int wcount = count; + while (wcount > 0){ + int n = MonoIO.Write (handle, src, offset, wcount, out error); + if (error != MonoIOError.ERROR_SUCCESS) + throw MonoIO.GetException (GetSecureFileName (name), error); + + wcount -= n; + offset += n; + } buf_start += count; } else { int copied = 0; while (count > 0) { - int n = WriteSegment (src, src_offset + copied, count); + int n = WriteSegment (src, offset + copied, count); copied += n; count -= n; @@ -596,8 +686,8 @@ namespace System.IO delegate void WriteDelegate (byte [] buffer, int offset, int count); - public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count, - AsyncCallback cback, object state) + public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes, + AsyncCallback userCallback, object stateObject) { if (handle == MonoIO.InvalidHandle) throw new ObjectDisposedException ("Stream has been closed"); @@ -605,58 +695,58 @@ namespace System.IO if (!CanWrite) throw new NotSupportedException ("This stream does not support writing"); - if (buffer == null) - throw new ArgumentNullException ("buffer"); + if (array == null) + throw new ArgumentNullException ("array"); - if (count < 0) - throw new ArgumentOutOfRangeException ("count", "Must be >= 0"); + if (numBytes < 0) + throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0"); if (offset < 0) throw new ArgumentOutOfRangeException ("offset", "Must be >= 0"); // reordered to avoid possible integer overflow - if (count > buffer.Length - offset) - throw new ArgumentException ("Buffer too small. count/offset wrong."); + if (numBytes > array.Length - offset) + throw new ArgumentException ("array too small. numBytes/offset wrong."); if (!async) - return base.BeginWrite (buffer, offset, count, cback, state); + return base.BeginWrite (array, offset, numBytes, userCallback, stateObject); - FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state); + FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject); result.BytesRead = -1; - result.Count = count; - result.OriginalCount = count; + result.Count = numBytes; + result.OriginalCount = numBytes; if (buf_dirty) { MemoryStream ms = new MemoryStream (); - FlushBufferToStream (ms); - ms.Write (buffer, offset, count); + FlushBuffer (ms); + ms.Write (array, offset, numBytes); offset = 0; - count = (int) ms.Length; + numBytes = (int) ms.Length; } WriteDelegate w = new WriteDelegate (WriteInternal); - return w.BeginInvoke (buffer, offset, count, cback, state); + return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject); } - public override void EndWrite (IAsyncResult async_result) + public override void EndWrite (IAsyncResult asyncResult) { - if (async_result == null) - throw new ArgumentNullException ("async_result"); + if (asyncResult == null) + throw new ArgumentNullException ("asyncResult"); if (!async) { - base.EndWrite (async_result); + base.EndWrite (asyncResult); return; } - AsyncResult ares = async_result as AsyncResult; + AsyncResult ares = asyncResult as AsyncResult; if (ares == null) - throw new ArgumentException ("Invalid IAsyncResult", "async_result"); + throw new ArgumentException ("Invalid IAsyncResult", "asyncResult"); WriteDelegate w = ares.AsyncDelegate as WriteDelegate; if (w == null) - throw new ArgumentException ("Invalid IAsyncResult", "async_result"); + throw new ArgumentException ("Invalid IAsyncResult", "asyncResult"); - w.EndInvoke (async_result); + w.EndInvoke (asyncResult); return; } @@ -712,14 +802,13 @@ namespace System.IO if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } return(buf_start); } - public override void SetLength (long length) + public override void SetLength (long value) { if (handle == MonoIO.InvalidHandle) throw new ObjectDisposedException ("Stream has been closed"); @@ -730,22 +819,21 @@ namespace System.IO if(CanWrite == false) throw new NotSupportedException("The stream does not support writing"); - if(length < 0) - throw new ArgumentOutOfRangeException("Length is less than 0"); + if(value < 0) + throw new ArgumentOutOfRangeException("value is less than 0"); Flush (); MonoIOError error; - MonoIO.SetLength (handle, length, out error); + MonoIO.SetLength (handle, value, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } - if (Position > length) - Position = length; + if (Position > value) + Position = value; } public override void Flush () @@ -754,19 +842,20 @@ namespace System.IO throw new ObjectDisposedException ("Stream has been closed"); FlushBuffer (); - - // The flushing is not actually required, in - //the mono runtime we were mapping flush to - //`fsync' which is not the same. - // - //MonoIO.Flush (handle); } - public override void Close () +#if NET_4_0 + public virtual void Flush (bool flushToDisk) { - Dispose (true); - GC.SuppressFinalize (this); // remove from finalize queue + FlushBuffer (); + + // This does the fsync + if (flushToDisk){ + MonoIOError error; + MonoIO.Flush (handle, out error); + } } +#endif public virtual void Lock (long position, long length) { @@ -787,8 +876,7 @@ namespace System.IO MonoIO.Lock (handle, position, length, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } } @@ -808,8 +896,7 @@ namespace System.IO MonoIO.Unlock (handle, position, length, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } } @@ -820,14 +907,15 @@ namespace System.IO Dispose (false); } -#if NET_2_0 protected override void Dispose (bool disposing) -#else - protected virtual void Dispose (bool disposing) -#endif { + Exception exc = null; if (handle != MonoIO.InvalidHandle) { - FlushBuffer (); + try { + FlushBuffer (); + } catch (Exception e) { + exc = e; + } if (owner) { MonoIOError error; @@ -835,8 +923,7 @@ namespace System.IO MonoIO.Close (handle, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } handle = MonoIO.InvalidHandle; @@ -845,10 +932,34 @@ namespace System.IO canseek = false; access = 0; - if (disposing) { + + if (disposing && buf != null) { + if (buf.Length == DefaultBufferSize && buf_recycle == null) { + lock (buf_recycle_lock) { + if (buf_recycle == null) { + buf_recycle = buf; + } + } + } + buf = null; + GC.SuppressFinalize (this); } + if (exc != null) + throw exc; + } + +#if !NET_2_1 + public FileSecurity GetAccessControl () + { + throw new NotImplementedException (); } + + public void SetAccessControl (FileSecurity fileSecurity) + { + throw new NotImplementedException (); + } +#endif // private. @@ -895,21 +1006,35 @@ namespace System.IO return(count); } - void FlushBufferToStream (Stream st) + void FlushBuffer (Stream st) { if (buf_dirty) { + MonoIOError error; + if (CanSeek == true) { - MonoIOError error; MonoIO.Seek (handle, buf_start, SeekOrigin.Begin, out error); if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); + } + } + if (st == null) { + int wcount = buf_length; + int offset = 0; + while (wcount > 0){ + int n = MonoIO.Write (handle, buf, 0, buf_length, out error); + if (error != MonoIOError.ERROR_SUCCESS) { + // don't leak the path information for isolated storage + throw MonoIO.GetException (GetSecureFileName (name), error); + } + wcount -= n; + offset += n; } + } else { + st.Write (buf, 0, buf_length); } - st.Write (buf, 0, buf_length); } buf_start += buf_offset; @@ -919,43 +1044,18 @@ namespace System.IO private void FlushBuffer () { - if (buf_dirty) { - MonoIOError error; - - if (CanSeek == true) { - MonoIO.Seek (handle, buf_start, - SeekOrigin.Begin, - out error); - if (error != MonoIOError.ERROR_SUCCESS) { - // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); - } - } - MonoIO.Write (handle, buf, 0, - buf_length, out error); - - if (error != MonoIOError.ERROR_SUCCESS) { - // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); - } - } - - buf_start += buf_offset; - buf_offset = buf_length = 0; - buf_dirty = false; + FlushBuffer (null); } private void FlushBufferIfDirty () { if (buf_dirty) - FlushBuffer (); + FlushBuffer (null); } private void RefillBuffer () { - FlushBuffer(); + FlushBuffer (null); buf_length = ReadData (handle, buf, 0, buf_size); @@ -974,8 +1074,7 @@ namespace System.IO amount = 0; // might not be needed, but well... } else if (error != MonoIOError.ERROR_SUCCESS) { // don't leak the path information for isolated storage - string fname = (anonymous) ? Path.GetFileName (name) : name; - throw MonoIO.GetException (fname, error); + throw MonoIO.GetException (GetSecureFileName (name), error); } /* Check for read error */ @@ -986,32 +1085,56 @@ namespace System.IO return(amount); } - private void InitBuffer (int size, bool noBuffering) + void InitBuffer (int size) { - if (noBuffering) { - size = 0; - // We need a buffer for the ReadByte method. This buffer won't - // be used for anything else since buf_size==0. - buf = new byte [1]; + if (size <= 0) + throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required."); + + size = Math.Max (size, 8); + + // + // Instead of allocating a new default buffer use the + // last one if there is any available + // + if (size <= DefaultBufferSize && buf_recycle != null) { + lock (buf_recycle_lock) { + if (buf_recycle != null) { + buf = buf_recycle; + buf_recycle = null; + } + } } - else { - if (size <= 0) - throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required."); - if (size < 8) - size = 8; + + if (buf == null) buf = new byte [size]; - } + else + Array.Clear (buf, 0, size); buf_size = size; - buf_start = 0; - buf_offset = buf_length = 0; - buf_dirty = false; +// buf_start = 0; +// buf_offset = buf_length = 0; +// buf_dirty = false; + } + + private string GetSecureFileName (string filename) + { + return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename); + } + + private string GetSecureFileName (string filename, bool full) + { + return (anonymous) ? Path.GetFileName (filename) : + (full) ? Path.GetFullPath (filename) : filename; } // fields internal const int DefaultBufferSize = 8192; + // Input buffer ready for recycling + static byte[] buf_recycle; + static readonly object buf_recycle_lock = new object (); + private FileAccess access; private bool owner; private bool async; @@ -1028,6 +1151,7 @@ namespace System.IO private string name = "[Unknown]"; // name of file. IntPtr handle; // handle to underlying file + SafeFileHandle safeHandle; // set only when using one of the + // constructors taking SafeFileHandle } } -