* File.cs: Changed argument names and thrown exception to better match
[mono.git] / mcs / class / corlib / System.IO / FileStream.cs
index a766152e6bc0cbaf8f359c857bb2736f9eacc29e..aebbc8645d7a27aba2da372eb579c57b2f0c96b1 100644 (file)
@@ -39,23 +39,39 @@ using System.Threading;
 
 #if NET_2_0
 using Microsoft.Win32.SafeHandles;
+using System.Security.AccessControl;
 #endif
 
 namespace System.IO
 {
+#if NET_2_0
+       [ComVisible (true)]
+#endif
        public class FileStream : Stream
        {
                // construct from handle
                
+#if NET_2_0_SAFEFILEHANDLE_ENABLED
+               [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
+#endif
                public FileStream (IntPtr handle, FileAccess access)
                        : this (handle, access, true, DefaultBufferSize, false) {}
 
+#if NET_2_0_SAFEFILEHANDLE_ENABLED
+               [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
+#endif
                public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
                        : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
                
+#if NET_2_0_SAFEFILEHANDLE_ENABLED
+               [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
+#endif
                public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
                        : this (handle, access, ownsHandle, bufferSize, false) {}
 
+#if NET_2_0_SAFEFILEHANDLE_ENABLED
+               [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
+#endif
                public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
                        : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
 
@@ -82,8 +98,6 @@ namespace System.IO
                                this.canseek = true;
                        } else {
                                this.canseek = false;
-                               noBuffering = true;
-                               bufferSize = 0;
                        }
 
                        this.handle = handle;
@@ -94,67 +108,129 @@ namespace System.IO
 
                        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;
                }
 
                // construct from filename
                
-               public FileStream (string name, FileMode mode)
-                       : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, false)
+               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 path, FileMode mode, FileAccess access)
+                       : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
+               {
+               }
+
+               public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
+                       : this (path, mode, access, share, DefaultBufferSize, 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 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, FileShare share, int bufferSize, bool isAsync)
+                       : this (path, mode, access, share, bufferSize, isAsync, FileOptions.None)
                {
                }
 
-               public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
-                       : this (name, mode, access, share, DefaultBufferSize, false, false)
+#if NET_2_0
+               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)
                {
                }
                
-               public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
-                       : this (name, mode, access, share, bufferSize, false, 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)
                {
                }
 
-               public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
-                       : this (name, mode, access, share, bufferSize, isAsync, false)
+               public FileStream (string path, FileMode mode,
+                                  FileSystemRights rights, FileShare share,
+                                  int bufferSize, FileOptions options)
+               {
+                       throw new NotImplementedException ();
+               }
+               
+               public FileStream (string path, FileMode mode,
+                                  FileSystemRights rights, FileShare share,
+                                  int bufferSize, FileOptions options,
+                                  FileSecurity fileSecurity)
                {
+                       throw new NotImplementedException ();
                }
+#endif
 
-               internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
+               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)
                {
-                       if (name == null) {
-                               throw new ArgumentNullException ("name");
+               }
+
+               internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
+               {
+                       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)
-                               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 NET_2_0
+                       if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete))
+#else
                        if (share < FileShare.None || share > FileShare.ReadWrite)
-                               throw new ArgumentOutOfRangeException ("share");
+#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);
+                               string fname = (anonymous) ? Path.GetFileName (path) : Path.GetFullPath (path);
                                throw new UnauthorizedAccessException (String.Format (msg, fname));
                        }
 
@@ -162,45 +238,48 @@ namespace System.IO
                         * 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));
+                       }
 
-                       string dname = Path.GetDirectoryName (name);
+                       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);
                                        throw new DirectoryNotFoundException (String.Format (msg, fname));
                                }
                        }
 
                        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 = (anonymous) ? Path.GetFileName (path) : Path.GetFullPath (path);
                                throw new FileNotFoundException (String.Format (msg, fname), fname);
                        }
 
                        // 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, false, 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;
+                               string fname = (anonymous) ? Path.GetFileName (path) : Path.GetFullPath (path);
                                throw MonoIO.GetException (fname, error);
                        }
 
@@ -212,7 +291,7 @@ namespace System.IO
                        
                        if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
                                this.canseek = true;
-                               this.async = isAsync;
+                               this.async = (options & FileOptions.Asynchronous) != 0;
                        } else {
                                this.canseek = false;
                                this.async = false;
@@ -246,18 +325,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 {
@@ -322,6 +401,9 @@ namespace System.IO
                        }
                }
 
+#if NET_2_0
+               [Obsolete ("Use SafeFileHandle instead")]
+#endif
                public virtual IntPtr Handle {
                        [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
                        [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
@@ -374,6 +456,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;
@@ -477,7 +567,7 @@ namespace System.IO
                                return base.BeginRead (buffer, offset, count, cback, state);
 
                        ReadDelegate r = new ReadDelegate (ReadInternal);
-                       return r.BeginInvoke (buffer, offset, count, cback, state);                     
+                       return r.BeginInvoke (buffer, offset, count, cback, state);
                }
                
                public override int EndRead (IAsyncResult async_result)
@@ -666,14 +756,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;
@@ -734,11 +816,12 @@ namespace System.IO
                        //MonoIO.Flush (handle);
                }
 
+#if !NET_2_0
                public override void Close ()
                {
                        Dispose (true);
-                       GC.SuppressFinalize (this);     // remove from finalize queue
                }
+#endif
 
                public virtual void Lock (long position, long length)
                {
@@ -792,7 +875,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 ();
 
@@ -815,8 +903,22 @@ namespace System.IO
                        if (disposing) {
                                buf = null;
                        }
+                       if (disposing)
+                               GC.SuppressFinalize (this);
                }
 
+#if NET_2_0
+               public FileSecurity GetAccessControl ()
+               {
+                       throw new NotImplementedException ();
+               }
+               
+               public void SetAccessControl (FileSecurity fileSecurity)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+
                // private.
 
                // ReadSegment, WriteSegment, FlushBuffer,
@@ -997,4 +1099,3 @@ namespace System.IO
                IntPtr handle;                          // handle to underlying file
        }
 }
-