2 // System.IO.FileStream.cs
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Dan Lewis (dihlewis@yahoo.co.uk)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // Marek Safar (marek.safar@gmail.com)
10 // (C) 2001-2003 Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004-2005, 2008, 2010 Novell, Inc (http://www.novell.com)
12 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System
.Collections
;
35 using System
.Globalization
;
36 using System
.Runtime
.CompilerServices
;
37 using System
.Runtime
.InteropServices
;
38 using System
.Runtime
.Remoting
.Messaging
;
39 using System
.Security
;
40 using System
.Security
.AccessControl
;
41 using System
.Security
.Permissions
;
42 using System
.Threading
;
43 using System
.Threading
.Tasks
;
44 using Microsoft
.Win32
.SafeHandles
;
49 public class FileStream
: Stream
51 // construct from handle
53 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
54 public FileStream (IntPtr handle
, FileAccess access
)
55 : this (handle
, access
, true, DefaultBufferSize
, false, false) {}
57 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
58 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
)
59 : this (handle
, access
, ownsHandle
, DefaultBufferSize
, false, false) {}
61 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
62 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
)
63 : this (handle
, access
, ownsHandle
, bufferSize
, false, false) {}
65 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
66 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
, bool isAsync
)
67 : this (handle
, access
, ownsHandle
, bufferSize
, isAsync
, false) {}
69 [SecurityPermission (SecurityAction
.Demand
, UnmanagedCode
= true)]
70 internal FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
, bool isAsync
, bool isConsoleWrapper
)
72 if (handle
== MonoIO
.InvalidHandle
)
73 throw new ArgumentException ("handle", Locale
.GetText ("Invalid."));
75 Init (new SafeFileHandle (handle
, false), access
, ownsHandle
, bufferSize
, isAsync
, isConsoleWrapper
);
78 // construct from filename
80 public FileStream (string path
, FileMode mode
)
81 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), FileShare
.Read
, DefaultBufferSize
, false, FileOptions
.None
)
85 public FileStream (string path
, FileMode mode
, FileAccess access
)
86 : this (path
, mode
, access
, access
== FileAccess
.Write
? FileShare
.None
: FileShare
.Read
, DefaultBufferSize
, false, false)
90 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
)
91 : this (path
, mode
, access
, share
, DefaultBufferSize
, false, FileOptions
.None
)
95 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
)
96 : this (path
, mode
, access
, share
, bufferSize
, false, FileOptions
.None
)
100 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool useAsync
)
101 : this (path
, mode
, access
, share
, bufferSize
, useAsync
? FileOptions
.Asynchronous
: FileOptions
.None
)
105 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, FileOptions options
)
106 : this (path
, mode
, access
, share
, bufferSize
, false, options
)
110 public FileStream (SafeFileHandle handle
, FileAccess access
)
111 :this(handle
, access
, DefaultBufferSize
, false)
115 public FileStream (SafeFileHandle handle
, FileAccess access
,
117 :this(handle
, access
, bufferSize
, false)
121 public FileStream (SafeFileHandle handle
, FileAccess access
, int bufferSize
, bool isAsync
)
123 Init (handle
, access
, false, bufferSize
, isAsync
, false);
126 [MonoLimitation ("This ignores the rights parameter")]
127 public FileStream (string path
, FileMode mode
,
128 FileSystemRights rights
, FileShare share
,
129 int bufferSize
, FileOptions options
)
130 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), share
, bufferSize
, false, options
)
134 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
135 public FileStream (string path
, FileMode mode
,
136 FileSystemRights rights
, FileShare share
,
137 int bufferSize
, FileOptions options
,
138 FileSecurity fileSecurity
)
139 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), share
, bufferSize
, false, options
)
143 internal FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, FileOptions options
, string msgPath
, bool bFromProxy
, bool useLongPath
= false, bool checkHost
= false)
144 : this (path
, mode
, access
, share
, bufferSize
, false, options
)
148 internal FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool isAsync
, bool anonymous
)
149 : this (path
, mode
, access
, share
, bufferSize
, anonymous
, isAsync
? FileOptions
.Asynchronous
: FileOptions
.None
)
153 internal FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool anonymous
, FileOptions options
)
156 throw new ArgumentNullException ("path");
159 if (path
.Length
== 0) {
160 throw new ArgumentException ("Path is empty");
163 this.anonymous
= anonymous
;
164 // ignore the Inheritable flag
165 share
&= ~FileShare
.Inheritable
;
168 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
170 if (mode
< FileMode
.CreateNew
|| mode
> FileMode
.Append
) {
173 throw new ArgumentException ("mode", "Enum value was out of legal range.");
176 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
179 if (access
< FileAccess
.Read
|| access
> FileAccess
.ReadWrite
) {
180 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
183 if (share
< FileShare
.None
|| share
> (FileShare
.ReadWrite
| FileShare
.Delete
)) {
184 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
187 if (path
.IndexOfAny (Path
.InvalidPathChars
) != -1) {
188 throw new ArgumentException ("Name has invalid chars");
191 path
= Path
.InsecureGetFullPath (path
);
193 if (Directory
.Exists (path
)) {
194 // don't leak the path information for isolated storage
195 string msg
= Locale
.GetText ("Access to the path '{0}' is denied.");
196 throw new UnauthorizedAccessException (String
.Format (msg
, GetSecureFileName (path
, false)));
199 /* Append streams can't be read (see FileMode
202 if (mode
==FileMode
.Append
&&
203 (access
&FileAccess
.Read
)==FileAccess
.Read
) {
204 throw new ArgumentException("Append access can be requested only in write-only mode.");
207 if ((access
& FileAccess
.Write
) == 0 &&
208 (mode
!= FileMode
.Open
&& mode
!= FileMode
.OpenOrCreate
)) {
209 string msg
= Locale
.GetText ("Combining FileMode: {0} with " +
210 "FileAccess: {1} is invalid.");
211 throw new ArgumentException (string.Format (msg
, access
, mode
));
214 SecurityManager
.EnsureElevatedPermissions (); // this is a no-op outside moonlight
216 string dname
= Path
.GetDirectoryName (path
);
217 if (dname
.Length
> 0) {
218 string fp
= Path
.GetFullPath (dname
);
219 if (!Directory
.Exists (fp
)) {
220 // don't leak the path information for isolated storage
221 string msg
= Locale
.GetText ("Could not find a part of the path \"{0}\".");
222 string fname
= (anonymous
) ? dname
: Path
.GetFullPath (path
);
223 throw new DirectoryNotFoundException (String
.Format (msg
, fname
));
227 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
231 // TODO: demand permissions
235 var nativeHandle
= MonoIO
.Open (path
, mode
, access
, share
, options
, out error
);
237 if (nativeHandle
== MonoIO
.InvalidHandle
) {
238 // don't leak the path information for isolated storage
239 throw MonoIO
.GetException (GetSecureFileName (path
), error
);
242 this.safeHandle
= new SafeFileHandle (nativeHandle
, false);
244 this.access
= access
;
247 /* Can we open non-files by name? */
249 if (MonoIO
.GetFileType (safeHandle
, out error
) == MonoFileType
.Disk
) {
251 this.async = (options
& FileOptions
.Asynchronous
) != 0;
253 this.canseek
= false;
258 if (access
== FileAccess
.Read
&& canseek
&& (bufferSize
== DefaultBufferSize
)) {
259 /* Avoid allocating a large buffer for small files */
261 if (bufferSize
> len
) {
262 bufferSize
= (int)(len
< 1000 ? 1000 : len
);
266 InitBuffer (bufferSize
, false);
268 if (mode
==FileMode
.Append
) {
269 this.Seek (0, SeekOrigin
.End
);
270 this.append_startpos
=this.Position
;
272 this.append_startpos
=0;
276 private void Init (SafeFileHandle safeHandle
, FileAccess access
, bool ownsHandle
, int bufferSize
, bool isAsync
, bool isConsoleWrapper
)
278 if (!isConsoleWrapper
&& safeHandle
.IsInvalid
)
279 throw new ArgumentException(Environment
.GetResourceString("Arg_InvalidHandle"), "handle");
280 if (access
< FileAccess
.Read
|| access
> FileAccess
.ReadWrite
)
281 throw new ArgumentOutOfRangeException ("access");
282 if (!isConsoleWrapper
&& bufferSize
<= 0)
283 throw new ArgumentOutOfRangeException("bufferSize", Environment
.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
286 MonoFileType ftype
= MonoIO
.GetFileType (safeHandle
, out error
);
288 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
289 throw MonoIO
.GetException (name
, error
);
292 if (ftype
== MonoFileType
.Unknown
) {
293 throw new IOException ("Invalid handle.");
294 } else if (ftype
== MonoFileType
.Disk
) {
297 this.canseek
= false;
300 this.safeHandle
= safeHandle
;
302 this.access
= access
;
303 this.owner
= ownsHandle
;
304 this.async = isAsync
;
305 this.anonymous
= false;
308 buf_start
= MonoIO
.Seek (safeHandle
, 0, SeekOrigin
.Current
, out error
);
309 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
310 throw MonoIO
.GetException (name
, error
);
314 /* Can't set append mode */
315 this.append_startpos
=0;
320 public override bool CanRead
{
322 return access
== FileAccess
.Read
||
323 access
== FileAccess
.ReadWrite
;
327 public override bool CanWrite
{
329 return access
== FileAccess
.Write
||
330 access
== FileAccess
.ReadWrite
;
334 public override bool CanSeek
{
340 public virtual bool IsAsync
{
346 public virtual string Name
{
352 public override long Length
{
354 if (safeHandle
.IsClosed
)
355 throw new ObjectDisposedException ("Stream has been closed");
358 throw new NotSupportedException ("The stream does not support seeking");
360 // Buffered data might change the length of the stream
361 FlushBufferIfDirty ();
365 long length
= MonoIO
.GetLength (safeHandle
, out error
);
367 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
368 // don't leak the path information for isolated storage
369 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
376 public override long Position
{
378 if (safeHandle
.IsClosed
)
379 throw new ObjectDisposedException ("Stream has been closed");
381 if (CanSeek
== false)
382 throw new NotSupportedException("The stream does not support seeking");
385 return(buf_start
+ buf_offset
);
387 // If the handle was leaked outside we always ask the real handle
390 long ret
= MonoIO
.Seek (safeHandle
, 0, SeekOrigin
.Current
, out error
);
392 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
393 // don't leak the path information for isolated storage
394 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
400 if (value < 0) throw new ArgumentOutOfRangeException("value", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
402 Seek (value, SeekOrigin
.Begin
);
406 [Obsolete ("Use SafeFileHandle instead")]
407 public virtual IntPtr Handle
{
408 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
409 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
411 var handle
= safeHandle
.DangerousGetHandle ();
418 public virtual SafeFileHandle SafeFileHandle
{
419 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
420 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
432 InitBuffer (0, true);
437 public override int ReadByte ()
439 if (safeHandle
.IsClosed
)
440 throw new ObjectDisposedException ("Stream has been closed");
443 throw new NotSupportedException ("Stream does not support reading");
446 int n
= ReadData (safeHandle
, buf
, 0, 1);
447 if (n
== 0) return -1;
450 else if (buf_offset
>= buf_length
) {
457 return buf
[buf_offset
++];
460 public override void WriteByte (byte value)
462 if (safeHandle
.IsClosed
)
463 throw new ObjectDisposedException ("Stream has been closed");
466 throw new NotSupportedException ("Stream does not support writing");
468 if (buf_offset
== buf_size
)
471 if (buf_size
== 0) { // No buffering
479 buf
[buf_offset
++] = value;
480 if (buf_offset
> buf_length
)
481 buf_length
= buf_offset
;
486 public override int Read ([In
,Out
] byte[] array
, int offset
, int count
)
488 if (safeHandle
.IsClosed
)
489 throw new ObjectDisposedException ("Stream has been closed");
491 throw new ArgumentNullException ("array");
493 throw new NotSupportedException ("Stream does not support reading");
494 int len
= array
.Length
;
496 throw new ArgumentOutOfRangeException ("offset", "< 0");
498 throw new ArgumentOutOfRangeException ("count", "< 0");
500 throw new ArgumentException ("destination offset is beyond array size");
501 // reordered to avoid possible integer overflow
502 if (offset
> len
- count
)
503 throw new ArgumentException ("Reading would overrun buffer");
506 IAsyncResult ares
= BeginRead (array
, offset
, count
, null, null);
507 return EndRead (ares
);
510 return ReadInternal (array
, offset
, count
);
513 int ReadInternal (byte [] dest
, int offset
, int count
)
515 int n
= ReadSegment (dest
, offset
, count
);
523 if (count
> buf_size
) {
524 /* Read as much as we can, up
528 n
= ReadData (safeHandle
, dest
, offset
+n
, count
);
530 /* Make the next buffer read
531 * start from the right place
536 n
= ReadSegment (dest
, offset
+copied
, count
);
542 delegate int ReadDelegate (byte [] buffer
, int offset
, int count
);
544 public override IAsyncResult
BeginRead (byte [] array
, int offset
, int numBytes
,
545 AsyncCallback userCallback
, object stateObject
)
547 if (safeHandle
.IsClosed
)
548 throw new ObjectDisposedException ("Stream has been closed");
551 throw new NotSupportedException ("This stream does not support reading");
554 throw new ArgumentNullException ("array");
557 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
560 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
562 // reordered to avoid possible integer overflow
563 if (numBytes
> array
.Length
- offset
)
564 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
567 return base.BeginRead (array
, offset
, numBytes
, userCallback
, stateObject
);
569 ReadDelegate r
= new ReadDelegate (ReadInternal
);
570 return r
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
573 public override int EndRead (IAsyncResult asyncResult
)
575 if (asyncResult
== null)
576 throw new ArgumentNullException ("asyncResult");
579 return base.EndRead (asyncResult
);
581 AsyncResult ares
= asyncResult
as AsyncResult
;
583 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
585 ReadDelegate r
= ares
.AsyncDelegate
as ReadDelegate
;
587 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
589 return r
.EndInvoke (asyncResult
);
592 public override void Write (byte[] array
, int offset
, int count
)
594 if (safeHandle
.IsClosed
)
595 throw new ObjectDisposedException ("Stream has been closed");
597 throw new ArgumentNullException ("array");
599 throw new ArgumentOutOfRangeException ("offset", "< 0");
601 throw new ArgumentOutOfRangeException ("count", "< 0");
602 // ordered to avoid possible integer overflow
603 if (offset
> array
.Length
- count
)
604 throw new ArgumentException ("Reading would overrun buffer");
606 throw new NotSupportedException ("Stream does not support writing");
609 IAsyncResult ares
= BeginWrite (array
, offset
, count
, null, null);
614 WriteInternal (array
, offset
, count
);
617 void WriteInternal (byte [] src
, int offset
, int count
)
619 if (count
> buf_size
) {
620 // shortcut for long writes
625 if (CanSeek
&& !isExposed
) {
626 MonoIO
.Seek (safeHandle
, buf_start
, SeekOrigin
.Begin
, out error
);
627 if (error
!= MonoIOError
.ERROR_SUCCESS
)
628 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
634 int n
= MonoIO
.Write (safeHandle
, src
, offset
, wcount
, out error
);
635 if (error
!= MonoIOError
.ERROR_SUCCESS
)
636 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
647 int n
= WriteSegment (src
, offset
+ copied
, count
);
660 delegate void WriteDelegate (byte [] buffer
, int offset
, int count
);
662 public override IAsyncResult
BeginWrite (byte [] array
, int offset
, int numBytes
,
663 AsyncCallback userCallback
, object stateObject
)
665 if (safeHandle
.IsClosed
)
666 throw new ObjectDisposedException ("Stream has been closed");
669 throw new NotSupportedException ("This stream does not support writing");
672 throw new ArgumentNullException ("array");
675 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
678 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
680 // reordered to avoid possible integer overflow
681 if (numBytes
> array
.Length
- offset
)
682 throw new ArgumentException ("array too small. numBytes/offset wrong.");
685 return base.BeginWrite (array
, offset
, numBytes
, userCallback
, stateObject
);
687 FileStreamAsyncResult result
= new FileStreamAsyncResult (userCallback
, stateObject
);
688 result
.BytesRead
= -1;
689 result
.Count
= numBytes
;
690 result
.OriginalCount
= numBytes
;
693 MemoryStream ms = new MemoryStream ();
695 ms.Write (array, offset, numBytes);
697 // Set arguments to new compounded buffer
699 array = ms.ToArray ();
700 numBytes = array.Length;
703 WriteDelegate w
= WriteInternal
;
704 return w
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
707 public override void EndWrite (IAsyncResult asyncResult
)
709 if (asyncResult
== null)
710 throw new ArgumentNullException ("asyncResult");
713 base.EndWrite (asyncResult
);
717 AsyncResult ares
= asyncResult
as AsyncResult
;
719 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
721 WriteDelegate w
= ares
.AsyncDelegate
as WriteDelegate
;
723 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
725 w
.EndInvoke (asyncResult
);
729 public override long Seek (long offset
, SeekOrigin origin
)
733 if (safeHandle
.IsClosed
)
734 throw new ObjectDisposedException ("Stream has been closed");
738 if(CanSeek
== false) {
739 throw new NotSupportedException("The stream does not support seeking");
744 pos
= Length
+ offset
;
747 case SeekOrigin
.Current
:
748 pos
= Position
+ offset
;
751 case SeekOrigin
.Begin
:
756 throw new ArgumentException ("origin", "Invalid SeekOrigin");
760 /* LAMESPEC: shouldn't this be
761 * ArgumentOutOfRangeException?
763 throw new IOException("Attempted to Seek before the beginning of the stream");
766 if(pos
< this.append_startpos
) {
767 /* More undocumented crap */
768 throw new IOException("Can't seek back over pre-existing data in append mode");
775 buf_start
= MonoIO
.Seek (safeHandle
, pos
, SeekOrigin
.Begin
, out error
);
777 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
778 // don't leak the path information for isolated storage
779 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
785 public override void SetLength (long value)
787 if (safeHandle
.IsClosed
)
788 throw new ObjectDisposedException ("Stream has been closed");
791 throw new NotSupportedException("The stream does not support seeking");
793 if(CanWrite
== false)
794 throw new NotSupportedException("The stream does not support writing");
797 throw new ArgumentOutOfRangeException("value is less than 0");
803 MonoIO
.SetLength (safeHandle
, value, out error
);
804 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
805 // don't leak the path information for isolated storage
806 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
809 if (Position
> value)
813 public override void Flush ()
815 if (safeHandle
.IsClosed
)
816 throw new ObjectDisposedException ("Stream has been closed");
821 public virtual void Flush (bool flushToDisk
)
823 if (safeHandle
.IsClosed
)
824 throw new ObjectDisposedException ("Stream has been closed");
828 // This does the fsync
831 MonoIO
.Flush (safeHandle
, out error
);
835 public virtual void Lock (long position
, long length
)
837 if (safeHandle
.IsClosed
)
838 throw new ObjectDisposedException ("Stream has been closed");
840 throw new ArgumentOutOfRangeException ("position must not be negative");
843 throw new ArgumentOutOfRangeException ("length must not be negative");
848 MonoIO
.Lock (safeHandle
, position
, length
, out error
);
849 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
850 // don't leak the path information for isolated storage
851 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
855 public virtual void Unlock (long position
, long length
)
857 if (safeHandle
.IsClosed
)
858 throw new ObjectDisposedException ("Stream has been closed");
860 throw new ArgumentOutOfRangeException ("position must not be negative");
863 throw new ArgumentOutOfRangeException ("length must not be negative");
868 MonoIO
.Unlock (safeHandle
, position
, length
, out error
);
869 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
870 // don't leak the path information for isolated storage
871 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
882 protected override void Dispose (bool disposing
)
884 Exception exc
= null;
885 if (safeHandle
!= null && !safeHandle
.IsClosed
) {
887 // If the FileStream is in "exposed" status
888 // it means that we do not have a buffer(we write the data without buffering)
889 // therefor we don't and can't flush the buffer becouse we don't have one.
891 } catch (Exception e
) {
898 MonoIO
.Close (safeHandle
.DangerousGetHandle (), out error
);
899 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
900 // don't leak the path information for isolated storage
901 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
904 safeHandle
.DangerousRelease ();
911 if (disposing
&& buf
!= null) {
912 if (buf
.Length
== DefaultBufferSize
&& buf_recycle
== null) {
913 lock (buf_recycle_lock
) {
914 if (buf_recycle
== null) {
921 GC
.SuppressFinalize (this);
927 public FileSecurity
GetAccessControl ()
929 if (safeHandle
.IsClosed
)
930 throw new ObjectDisposedException ("Stream has been closed");
932 return new FileSecurity (SafeFileHandle
,
933 AccessControlSections
.Owner
|
934 AccessControlSections
.Group
|
935 AccessControlSections
.Access
);
938 public void SetAccessControl (FileSecurity fileSecurity
)
940 if (safeHandle
.IsClosed
)
941 throw new ObjectDisposedException ("Stream has been closed");
943 if (null == fileSecurity
)
944 throw new ArgumentNullException ("fileSecurity");
946 fileSecurity
.PersistModifications (SafeFileHandle
);
949 public override Task
FlushAsync (CancellationToken cancellationToken
)
951 if (safeHandle
.IsClosed
)
952 throw new ObjectDisposedException ("Stream has been closed");
954 return base.FlushAsync (cancellationToken
);
957 public override Task
<int> ReadAsync (byte[] buffer
, int offset
, int count
, CancellationToken cancellationToken
)
959 return base.ReadAsync (buffer
, offset
, count
, cancellationToken
);
962 public override Task
WriteAsync (byte[] buffer
, int offset
, int count
, CancellationToken cancellationToken
)
964 return base.WriteAsync (buffer
, offset
, count
, cancellationToken
);
969 // ReadSegment, WriteSegment, FlushBuffer,
970 // RefillBuffer and ReadData should only be called
971 // when the Monitor lock is held, but these methods
972 // grab it again just to be safe.
974 private int ReadSegment (byte [] dest
, int dest_offset
, int count
)
976 count
= Math
.Min (count
, buf_length
- buf_offset
);
979 // Use the fastest method, all range checks has been done
980 Buffer
.InternalBlockCopy (buf
, buf_offset
, dest
, dest_offset
, count
);
987 private int WriteSegment (byte [] src
, int src_offset
,
990 if (count
> buf_size
- buf_offset
) {
991 count
= buf_size
- buf_offset
;
995 Buffer
.BlockCopy (src
, src_offset
,
999 if (buf_offset
> buf_length
) {
1000 buf_length
= buf_offset
;
1012 // if (st == null) {
1015 if (CanSeek
== true && !isExposed
) {
1016 MonoIO
.Seek (safeHandle
, buf_start
, SeekOrigin
.Begin
, out error
);
1018 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1019 // don't leak the path information for isolated storage
1020 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1024 int wcount
= buf_length
;
1027 int n
= MonoIO
.Write (safeHandle
, buf
, offset
, buf_length
, out error
);
1028 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1029 // don't leak the path information for isolated storage
1030 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1036 // st.Write (buf, 0, buf_length);
1040 buf_start
+= buf_offset
;
1041 buf_offset
= buf_length
= 0;
1045 private void FlushBufferIfDirty ()
1051 private void RefillBuffer ()
1055 buf_length
= ReadData (safeHandle
, buf
, 0, buf_size
);
1058 private int ReadData (SafeHandle safeHandle
, byte[] buf
, int offset
,
1064 /* when async == true, if we get here we don't suport AIO or it's disabled
1065 * and we're using the threadpool */
1066 amount
= MonoIO
.Read (safeHandle
, buf
, offset
, count
, out error
);
1067 if (error
== MonoIOError
.ERROR_BROKEN_PIPE
) {
1068 amount
= 0; // might not be needed, but well...
1069 } else if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1070 // don't leak the path information for isolated storage
1071 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1074 /* Check for read error */
1076 throw new IOException ();
1082 void InitBuffer (int size
, bool isZeroSize
)
1089 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1091 size
= Math
.Max (size
, 8);
1094 // Instead of allocating a new default buffer use the
1095 // last one if there is any available
1097 if (size
<= DefaultBufferSize
&& buf_recycle
!= null) {
1098 lock (buf_recycle_lock
) {
1099 if (buf_recycle
!= null) {
1107 buf
= new byte [size
];
1109 Array
.Clear (buf
, 0, size
);
1114 // buf_offset = buf_length = 0;
1115 // buf_dirty = false;
1118 private string GetSecureFileName (string filename
)
1120 return (anonymous
) ? Path
.GetFileName (filename
) : Path
.GetFullPath (filename
);
1123 private string GetSecureFileName (string filename
, bool full
)
1125 return (anonymous
) ? Path
.GetFileName (filename
) :
1126 (full
) ? Path
.GetFullPath (filename
) : filename
;
1131 internal const int DefaultBufferSize
= 4096;
1133 // Input buffer ready for recycling
1134 static byte[] buf_recycle
;
1135 static readonly object buf_recycle_lock
= new object ();
1137 private byte [] buf
; // the buffer
1138 private string name
= "[Unknown]"; // name of file.
1140 private SafeFileHandle safeHandle
;
1141 private bool isExposed
;
1143 private long append_startpos
;
1145 private FileAccess access
;
1148 private bool canseek
;
1149 private bool anonymous
;
1150 private bool buf_dirty
; // true if buffer has been written to
1152 private int buf_size
; // capacity in bytes
1153 private int buf_length
; // number of valid bytes in buffer
1154 private int buf_offset
; // position of next byte
1155 private long buf_start
; // location of buffer in file