New test.
[mono.git] / mcs / class / corlib / System.IO / FileStream.cs
index 1cdeb8e7005f5b040f3f63be8cebd83078f6abe9..0c504f56ee09e68d4e21e771e3f17f89288aa956 100644 (file)
@@ -7,16 +7,40 @@
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
 // (C) 2001-2003 Ximian, Inc.  http://www.ximian.com
-// (c) 2004 Novell, Inc. (http://www.novell.com)
+// Copyright (C) 2004-2005 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.Collections;
+using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Remoting.Messaging;
+using System.Security.Permissions;
 using System.Threading;
 
+#if NET_2_0
+using Microsoft.Win32.SafeHandles;
+#endif
+
 namespace System.IO
 {
        public class FileStream : Stream
@@ -35,15 +59,22 @@ namespace System.IO
                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)
                {
                        this.handle = MonoIO.InvalidHandle;
+                       if (handle == this.handle)
+                               throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
 
                        if (access < FileAccess.Read || access > FileAccess.ReadWrite)
                                throw new ArgumentOutOfRangeException ("access");
 
                        MonoIOError error;
                        MonoFileType ftype = MonoIO.GetFileType (handle, out error);
+
+                       if (error != MonoIOError.ERROR_SUCCESS) {
+                               throw MonoIO.GetException (name, error);
+                       }
                        
                        if (ftype == MonoFileType.Unknown) {
                                throw new IOException ("Invalid handle.");
@@ -51,18 +82,25 @@ 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 (isAsync && MonoIO.SupportsAsync)
-                               ThreadPool.BindHandle (handle);
+                       this.anonymous = false;
 
                        InitBuffer (bufferSize, noBuffering);
 
+                       if (canseek) {
+                               buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
+                               if (error != MonoIOError.ERROR_SUCCESS) {
+                                       throw MonoIO.GetException (name, error);
+                               }
+                       }
+
                        /* Can't set append mode */
                        this.append_startpos=0;
                }
@@ -70,90 +108,139 @@ 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) { }
+                       : this (name, 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, FileShare.ReadWrite, DefaultBufferSize, false) { }
+                       : this (name, 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) { }
+                       : this (name, 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) { }
+                       : this (name, mode, access, share, bufferSize, false, 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_0
+               public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+                       : this (name, mode, access, 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 name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
                {
                        if (name == null) {
                                throw new ArgumentNullException ("name");
                        }
                        
-                       if (name == "") {
+                       if (name.Length == 0) {
                                throw new ArgumentException ("Name is empty");
                        }
 
+#if NET_2_0
+                       // ignore the Inheritable flag
+                       share &= ~FileShare.Inheritable;
+
+#endif
+
+                       if (bufferSize <= 0)
+                               throw new ArgumentOutOfRangeException ("Positive number required.");
+
                        if (mode < FileMode.CreateNew || mode > FileMode.Append)
-                               throw new ArgumentOutOfRangeException ("mode");
+                               throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
 
                        if (access < FileAccess.Read || access > FileAccess.ReadWrite)
-                               throw new ArgumentOutOfRangeException ("access");
+                               throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
 
                        if (share < FileShare.None || share > FileShare.ReadWrite)
-                               throw new ArgumentOutOfRangeException ("share");
+                               throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
 
                        if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
                                throw new ArgumentException ("Name has invalid chars");
                        }
 
                        if (Directory.Exists (name)) {
-                               throw new UnauthorizedAccessException ("Access to the path '" + Path.GetFullPath (name) + "' is denied.");
+                               // 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));
                        }
 
                        /* Append streams can't be read (see FileMode
                         * docs)
                         */
                        if (mode==FileMode.Append &&
-                           (access&FileAccess.Read)==FileAccess.Read) {
-                               throw new ArgumentException("Append streams can not be read");
+                               (access&FileAccess.Read)==FileAccess.Read) {
+                               throw new ArgumentException("Append access can be requested only in write-only mode.");
                        }
 
                        if ((access & FileAccess.Write) == 0 &&
-                           (mode != FileMode.Open && mode != FileMode.OpenOrCreate))
-                               throw new ArgumentException ("access and mode not compatible");
+                               (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
+                               string msg = Locale.GetText ("Combining FileMode: {0} with " +
+                                       "FileAccess: {1} is invalid.");
+                               throw new ArgumentException (string.Format (msg, access, mode));
+                       }
 
-                       if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
-                                       mode != FileMode.CreateNew && !File.Exists (name))
-                               throw new FileNotFoundException ("Could not find file \"" + name + "\".");
+                       string dname = Path.GetDirectoryName (name);
+                       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;
+                                       throw new DirectoryNotFoundException (String.Format (msg, fname));
+                               }
+                       }
 
-                       if (mode == FileMode.CreateNew) {
-                               string dname = Path.GetDirectoryName (name);
-                               string fp = null; ;
-                               if (dname != "" && !Directory.Exists ((fp = Path.GetFullPath (dname))))
-                                       throw new DirectoryNotFoundException ("Could not find a part of " +
-                                                                       "the path \"" + fp + "\".");
+                       if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
+                                       mode != FileMode.CreateNew && !File.Exists (name)) {
+                               // 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;
+                               throw new FileNotFoundException (String.Format (msg, fname), fname);
                        }
 
-                       this.name = name;
+                       // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
+                       if (!anonymous)
+                               this.name = name;
 
                        // TODO: demand permissions
 
                        MonoIOError error;
 
-                       bool openAsync = (isAsync && MonoIO.SupportsAsync);
-                       this.handle = MonoIO.Open (name, mode, access, share, openAsync, out error);
+                       this.handle = MonoIO.Open (name, mode, access, share, options, out error);
                        if (handle == MonoIO.InvalidHandle) {
-                               throw MonoIO.GetException (name, error);
+                               // don't leak the path information for isolated storage
+                               string fname = (anonymous) ? Path.GetFileName (name) : name;
+                               throw MonoIO.GetException (fname, error);
                        }
 
                        this.access = access;
                        this.owner = true;
+                       this.anonymous = anonymous;
 
                        /* Can we open non-files by name? */
                        
                        if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
                                this.canseek = true;
-                               this.async = isAsync;
-                               if (openAsync)
-                                       ThreadPool.BindHandle (handle);
+                               this.async = (options & FileOptions.Asynchronous) != 0;
                        } else {
                                this.canseek = false;
                                this.async = false;
@@ -217,15 +304,23 @@ namespace System.IO
                                if (handle == MonoIO.InvalidHandle)
                                        throw new ObjectDisposedException ("Stream has been closed");
 
-                               if (!canseek)
+                               if (!CanSeek)
                                        throw new NotSupportedException ("The stream does not support seeking");
 
                                // Buffered data might change the length of the stream
                                FlushBufferIfDirty ();
 
                                MonoIOError error;
+                               long length;
                                
-                               return MonoIO.GetLength (handle, out error);
+                               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);
+                               }
+
+                               return(length);
                        }
                }
 
@@ -240,6 +335,9 @@ namespace System.IO
                                return(buf_start + buf_offset);
                        }
                        set {
+                               if (handle == MonoIO.InvalidHandle)
+                                       throw new ObjectDisposedException ("Stream has been closed");
+
                                if(CanSeek == false) {
                                        throw new NotSupportedException("The stream does not support seeking");
                                }
@@ -253,11 +351,21 @@ namespace System.IO
                }
 
                public virtual IntPtr Handle {
+                       [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
+                       [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
                        get {
                                return handle;
                        }
                }
 
+#if NET_2_0
+               public virtual SafeFileHandle SafeFileHandle {
+                       [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
+                       [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
+                       get { throw new NotImplementedException (); }
+               }
+#endif
+
                // methods
 
                public override int ReadByte ()
@@ -294,6 +402,14 @@ namespace System.IO
                        if (buf_offset == buf_size)
                                FlushBuffer ();
 
+                       if (buf_size == 0) { // No buffering
+                               buf [0] = value;
+                               buf_dirty = true;
+                               buf_length = 1;
+                               FlushBuffer ();
+                               return;
+                       }
+
                        buf [buf_offset ++] = value;
                        if (buf_offset > buf_length)
                                buf_length = buf_offset;
@@ -310,8 +426,10 @@ namespace System.IO
                        if (!CanRead)
                                throw new NotSupportedException ("Stream does not support reading");
                        int len = dest.Length;
-                       if (dest_offset < 0 || count < 0)
-                               throw new ArgumentException ("dest or count is negative");
+                       if (dest_offset < 0)
+                               throw new ArgumentOutOfRangeException ("dest_offset", "< 0");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", "< 0");
                        if (dest_offset > len)
                                throw new ArgumentException ("destination offset is beyond array size");
                        // reordered to avoid possible integer overflow
@@ -372,6 +490,9 @@ namespace System.IO
                public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
                                                        AsyncCallback cback, object state)
                {
+                       if (handle == MonoIO.InvalidHandle)
+                               throw new ObjectDisposedException ("Stream has been closed");
+
                        if (!CanRead)
                                throw new NotSupportedException ("This stream does not support reading");
 
@@ -391,28 +512,8 @@ namespace System.IO
                        if (!async)
                                return base.BeginRead (buffer, offset, count, cback, state);
 
-                       if (!MonoIO.SupportsAsync) {
-                               ReadDelegate r = new ReadDelegate (ReadInternal);
-                               return r.BeginInvoke (buffer, offset, count, cback, state);                     
-                       }
-
-                       FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
-                       result.Count = count;
-                       result.OriginalCount = count;
-                       int buffered = ReadSegment (buffer, offset, count);
-                       if (buffered >= count) {
-                               result.SetComplete (null, buffered, true);
-                               return result;
-                       }
-                       
-                       result.Buffer = buffer;
-                       result.Offset = offset + buffered;
-                       result.Count -= buffered;
-                       
-                       KeepReference (result);
-                       MonoIO.BeginRead (handle, result);
-
-                       return result;
+                       ReadDelegate r = new ReadDelegate (ReadInternal);
+                       return r.BeginInvoke (buffer, offset, count, cback, state);                     
                }
                
                public override int EndRead (IAsyncResult async_result)
@@ -423,35 +524,15 @@ namespace System.IO
                        if (!async)
                                return base.EndRead (async_result);
 
-                       if (!MonoIO.SupportsAsync) {
-                               AsyncResult ares = async_result as AsyncResult;
-                               if (ares == null)
-                                       throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
-                               ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
-                               if (r == null)
-                                       throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
-                               return r.EndInvoke (async_result);
-                       }
-
-                       FileStreamAsyncResult result = async_result as FileStreamAsyncResult;
-                       if (result == null || result.BytesRead == -1)
+                       AsyncResult ares = async_result as AsyncResult;
+                       if (ares == null)
                                throw new ArgumentException ("Invalid IAsyncResult", "async_result");
 
-                       RemoveReference (result);
-                       if (result.Done)
-                               throw new InvalidOperationException ("EndRead already called.");
-
-                       result.Done = true;
-                       if (!result.IsCompleted)
-                               result.AsyncWaitHandle.WaitOne ();
-
-                       if (result.Exception != null)
-                               throw result.Exception;
+                       ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
+                       if (r == null)
+                               throw new ArgumentException ("Invalid IAsyncResult", "async_result");
 
-                       buf_start += result.BytesRead;
-                       return result.OriginalCount - result.Count + result.BytesRead;
+                       return r.EndInvoke (async_result);
                }
 
                public override void Write (byte[] src, int src_offset, int count)
@@ -488,6 +569,12 @@ namespace System.IO
                                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);
+                               }
+                               
                                buf_start += count;
                        } else {
 
@@ -512,6 +599,9 @@ namespace System.IO
                public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
                                                        AsyncCallback cback, object state)
                {
+                       if (handle == MonoIO.InvalidHandle)
+                               throw new ObjectDisposedException ("Stream has been closed");
+
                        if (!CanWrite)
                                throw new NotSupportedException ("This stream does not support writing");
 
@@ -531,8 +621,6 @@ namespace System.IO
                        if (!async)
                                return base.BeginWrite (buffer, offset, count, cback, state);
 
-                       byte [] bytes;
-                       int buffered = 0;
                        FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
                        result.BytesRead = -1;
                        result.Count = count;
@@ -541,33 +629,13 @@ namespace System.IO
                        if (buf_dirty) {
                                MemoryStream ms = new MemoryStream ();
                                FlushBufferToStream (ms);
-                               buffered = (int) ms.Length;
                                ms.Write (buffer, offset, count);
-                               bytes = ms.GetBuffer ();
                                offset = 0;
                                count = (int) ms.Length;
-                       } else {
-                               bytes = buffer;
-                       }
-
-                       if (!MonoIO.SupportsAsync) {
-                               WriteDelegate w = new WriteDelegate (WriteInternal);
-                               return w.BeginInvoke (buffer, offset, count, cback, state);                     
                        }
 
-                       if (buffered >= count) {
-                               result.SetComplete (null, buffered, true);
-                               return result;
-                       }
-                       
-                       result.Buffer = buffer;
-                       result.Offset = offset;
-                       result.Count = count;
-                       
-                       KeepReference (result);
-                       MonoIO.BeginWrite (handle, result);
-
-                       return result;
+                       WriteDelegate w = new WriteDelegate (WriteInternal);
+                       return w.BeginInvoke (buffer, offset, count, cback, state);                     
                }
                
                public override void EndWrite (IAsyncResult async_result)
@@ -580,36 +648,16 @@ namespace System.IO
                                return;
                        }
 
-                       if (!MonoIO.SupportsAsync) {
-                               AsyncResult ares = async_result as AsyncResult;
-                               if (ares == null)
-                                       throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
-                               WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
-                               if (w == null)
-                                       throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
-                               w.EndInvoke (async_result);
-                               return;
-                       }
-
-                       FileStreamAsyncResult result = async_result as FileStreamAsyncResult;
-                       if (result == null || result.BytesRead != -1)
+                       AsyncResult ares = async_result as AsyncResult;
+                       if (ares == null)
                                throw new ArgumentException ("Invalid IAsyncResult", "async_result");
 
-                       RemoveReference (result);
-                       if (result.Done)
-                               throw new InvalidOperationException ("EndWrite already called.");
-
-                       result.Done = true;
-                       if (!result.IsCompleted)
-                               result.AsyncWaitHandle.WaitOne ();
-
-                       if (result.Exception != null)
-                               throw result.Exception;
+                       WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
+                       if (w == null)
+                               throw new ArgumentException ("Invalid IAsyncResult", "async_result");
 
-                       buf_start += result.Count;
-                       buf_offset = buf_length = 0;
+                       w.EndInvoke (async_result);
+                       return;
                }
 
                public override long Seek (long offset, SeekOrigin origin)
@@ -634,9 +682,12 @@ namespace System.IO
                                pos = Position + offset;
                                break;
 
-                       case SeekOrigin.Begin: default:
+                       case SeekOrigin.Begin:
                                pos = offset;
                                break;
+
+                       default:
+                               throw new ArgumentException ("origin", "Invalid SeekOrigin");
                        }
 
                        if (pos < 0) {
@@ -651,14 +702,6 @@ namespace System.IO
                                throw new IOException("Can't seek back over pre-existing data in append mode");
                        }
 
-                       if (buf_length > 0) {
-                               if (pos >= buf_start &&
-                                       pos <= buf_start + buf_length) {
-                                       buf_offset = (int) (pos - buf_start);
-                                       return pos;
-                               }
-                       }
-
                        FlushBuffer ();
 
                        MonoIOError error;
@@ -666,29 +709,43 @@ namespace System.IO
                        buf_start = MonoIO.Seek (handle, pos,
                                                 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);
+                       }
                        
                        return(buf_start);
                }
 
                public override void SetLength (long length)
                {
-                       if(CanSeek == false) {
+                       if (handle == MonoIO.InvalidHandle)
+                               throw new ObjectDisposedException ("Stream has been closed");
+
+                       if(CanSeek == false)
                                throw new NotSupportedException("The stream does not support seeking");
-                       }
 
-                       if(CanWrite == false) {
+                       if(CanWrite == false)
                                throw new NotSupportedException("The stream does not support writing");
-                       }
 
-                       if(length < 0) {
+                       if(length < 0)
                                throw new ArgumentOutOfRangeException("Length is less than 0");
-                       }
                        
                        Flush ();
 
                        MonoIOError error;
                        
                        MonoIO.SetLength (handle, 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);
+                       }
+
+                       if (Position > length)
+                               Position = length;
                }
 
                public override void Flush ()
@@ -711,16 +768,49 @@ namespace System.IO
                        GC.SuppressFinalize (this);     // remove from finalize queue
                }
 
-               [MonoTODO]
                public virtual void Lock (long position, long length)
                {
-                       throw new NotImplementedException ();
+                       if (handle == MonoIO.InvalidHandle)
+                               throw new ObjectDisposedException ("Stream has been closed");
+                       if (position < 0) {
+                               throw new ArgumentOutOfRangeException ("position must not be negative");
+                       }
+                       if (length < 0) {
+                               throw new ArgumentOutOfRangeException ("length must not be negative");
+                       }
+                       if (handle == MonoIO.InvalidHandle) {
+                               throw new ObjectDisposedException ("Stream has been closed");
+                       }
+                               
+                       MonoIOError error;
+
+                       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);
+                       }
                }
 
-               [MonoTODO]
                public virtual void Unlock (long position, long length)
                {
-                       throw new NotImplementedException ();
+                       if (handle == MonoIO.InvalidHandle)
+                               throw new ObjectDisposedException ("Stream has been closed");
+                       if (position < 0) {
+                               throw new ArgumentOutOfRangeException ("position must not be negative");
+                       }
+                       if (length < 0) {
+                               throw new ArgumentOutOfRangeException ("length must not be negative");
+                       }
+                               
+                       MonoIOError error;
+
+                       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);
+                       }
                }
 
                // protected
@@ -730,7 +820,12 @@ namespace System.IO
                        Dispose (false);
                }
 
-               protected virtual void Dispose (bool disposing) {
+#if NET_2_0
+               protected override void Dispose (bool disposing)
+#else
+               protected virtual void Dispose (bool disposing)
+#endif
+               {
                        if (handle != MonoIO.InvalidHandle) {
                                FlushBuffer ();
 
@@ -738,6 +833,11 @@ namespace System.IO
                                        MonoIOError error;
                                
                                        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);
+                                       }
 
                                        handle = MonoIO.InvalidHandle;
                                }
@@ -803,6 +903,11 @@ namespace System.IO
                                        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);
+                                       }
                                }
                                st.Write (buf, 0, buf_length);
                        }
@@ -821,9 +926,20 @@ namespace System.IO
                                        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;
@@ -854,15 +970,17 @@ namespace System.IO
                        /* when async == true, if we get here we don't suport AIO or it's disabled
                         * and we're using the threadpool */
                        amount = MonoIO.Read (handle, buf, offset, count, out error);
+                       if (error == MonoIOError.ERROR_BROKEN_PIPE) {
+                               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);
+                       }
                        
                        /* Check for read error */
                        if(amount == -1) {
-                               /* Kludge around broken pipes */
-                               if(error == MonoIOError.ERROR_BROKEN_PIPE) {
-                                       amount = 0;
-                               } else {
-                                       throw new IOException ();
-                               }
+                               throw new IOException ();
                        }
                        
                        return(amount);
@@ -890,37 +1008,16 @@ namespace System.IO
                        buf_dirty = false;
                }
 
-               static void KeepReference (object o)
-               {
-                       lock (typeof (FileStream)) {
-                               if (asyncObjects == null)
-                                       asyncObjects = new Hashtable ();
-
-                               asyncObjects [o] = o;
-                       }
-               }
-               
-               static void RemoveReference (object o)
-               {
-                       lock (typeof (FileStream)) {
-                               if (asyncObjects == null)
-                                       return;
-
-                               asyncObjects.Remove (o);
-                       }
-               }
-
                // fields
 
-               const int DefaultBufferSize = 8192;
-               private static Hashtable asyncObjects;
+               internal const int DefaultBufferSize = 8192;
 
                private FileAccess access;
                private bool owner;
                private bool async;
                private bool canseek;
                private long append_startpos;
-               
+               private bool anonymous;
 
                private byte [] buf;                    // the buffer
                private int buf_size;                   // capacity in bytes