2005-08-11 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.IO / FileStream.cs
index 1e23d05ea82f1f783397ade1d9a6e5c958799534..945f995d682a83bf69cd8e2612f102eae1dcd7c8 100644 (file)
 // 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
@@ -59,6 +59,7 @@ 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;
@@ -81,15 +82,15 @@ 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);
 
@@ -151,7 +152,10 @@ namespace System.IO
                        }
 
                        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
@@ -166,16 +170,23 @@ namespace System.IO
                            (mode != FileMode.Open && mode != FileMode.OpenOrCreate))
                                throw new ArgumentException ("access and mode not compatible");
 
+                       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 (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
-                                       mode != FileMode.CreateNew && !File.Exists (name))
-                               throw new FileNotFoundException ("Could not find file \"" + name + "\".", name);
-
-                       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 + "\".");
+                                       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);
                        }
 
                        // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
@@ -186,22 +197,22 @@ namespace System.IO
 
                        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, false, 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);
                        } else {
                                this.canseek = false;
                                this.async = false;
@@ -265,7 +276,7 @@ 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
@@ -276,8 +287,9 @@ namespace System.IO
                                
                                length = MonoIO.GetLength (handle, out error);
                                if (error != MonoIOError.ERROR_SUCCESS) {
-                                       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);
                                }
 
                                return(length);
@@ -311,6 +323,8 @@ namespace System.IO
                }
 
                public virtual IntPtr Handle {
+                       [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
+                       [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
                        get {
                                return handle;
                        }
@@ -318,6 +332,8 @@ 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 (); }
                }
 #endif
@@ -358,6 +374,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;
@@ -460,28 +484,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)
@@ -492,35 +496,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)
@@ -558,8 +542,9 @@ namespace System.IO
 
                                MonoIO.Write (handle, src, src_offset, count, out error);
                                if (error != MonoIOError.ERROR_SUCCESS) {
-                                       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);
                                }
                                
                                buf_start += count;
@@ -608,8 +593,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;
@@ -618,33 +601,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)
@@ -657,36 +620,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)
@@ -748,7 +691,9 @@ namespace System.IO
                                                 out error);
 
                        if (error != MonoIOError.ERROR_SUCCESS) {
-                               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);
                        }
                        
                        return(buf_start);
@@ -774,7 +719,9 @@ namespace System.IO
                        
                        MonoIO.SetLength (handle, length, out error);
                        if (error != MonoIOError.ERROR_SUCCESS) {
-                               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);
                        }
 
                        if (Position > length)
@@ -819,7 +766,9 @@ namespace System.IO
 
                        MonoIO.Lock (handle, position, length, out error);
                        if (error != MonoIOError.ERROR_SUCCESS) {
-                               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);
                        }
                }
 
@@ -838,7 +787,9 @@ namespace System.IO
 
                        MonoIO.Unlock (handle, position, length, out error);
                        if (error != MonoIOError.ERROR_SUCCESS) {
-                               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);
                        }
                }
 
@@ -858,7 +809,9 @@ namespace System.IO
                                
                                        MonoIO.Close (handle, out error);
                                        if (error != MonoIOError.ERROR_SUCCESS) {
-                                               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);
                                        }
 
                                        handle = MonoIO.InvalidHandle;
@@ -926,7 +879,9 @@ namespace System.IO
                                                     SeekOrigin.Begin,
                                                     out error);
                                        if (error != MonoIOError.ERROR_SUCCESS) {
-                                               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);
                                        }
                                }
                                st.Write (buf, 0, buf_length);
@@ -947,14 +902,18 @@ namespace System.IO
                                                     SeekOrigin.Begin,
                                                     out error);
                                        if (error != MonoIOError.ERROR_SUCCESS) {
-                                               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);
                                        }
                                }
                                MonoIO.Write (handle, buf, 0,
                                              buf_length, out error);
 
                                if (error != MonoIOError.ERROR_SUCCESS) {
-                                       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);
                                }
                        }
 
@@ -989,7 +948,9 @@ namespace System.IO
                        if (error == MonoIOError.ERROR_BROKEN_PIPE) {
                                amount = 0; // might not be needed, but well...
                        } else if (error != MonoIOError.ERROR_SUCCESS) {
-                               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);
                        }
                        
                        /* Check for read error */
@@ -1022,37 +983,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
 
                internal const int DefaultBufferSize = 8192;
-               private static Hashtable asyncObjects;
 
                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