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)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Collections
;
34 using System
.Globalization
;
35 using System
.Runtime
.CompilerServices
;
36 using System
.Runtime
.InteropServices
;
37 using System
.Runtime
.Remoting
.Messaging
;
38 using System
.Security
;
39 using System
.Security
.Permissions
;
40 using System
.Threading
;
42 using Microsoft
.Win32
.SafeHandles
;
44 using System
.IO
.IsolatedStorage
;
46 using System
.Security
.AccessControl
;
52 public class FileStream
: Stream
54 // construct from handle
56 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
57 public FileStream (IntPtr handle
, FileAccess access
)
58 : this (handle
, access
, true, DefaultBufferSize
, false) {}
60 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
61 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
)
62 : this (handle
, access
, ownsHandle
, DefaultBufferSize
, false) {}
64 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
65 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
)
66 : this (handle
, access
, ownsHandle
, bufferSize
, false) {}
68 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
69 public FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
, bool isAsync
)
70 : this (handle
, access
, ownsHandle
, bufferSize
, isAsync
, false) {}
72 [SecurityPermission (SecurityAction
.Demand
, UnmanagedCode
= true)]
73 internal FileStream (IntPtr handle
, FileAccess access
, bool ownsHandle
, int bufferSize
, bool isAsync
, bool isZeroSize
)
75 this.handle
= MonoIO
.InvalidHandle
;
76 if (handle
== this.handle
)
77 throw new ArgumentException ("handle", Locale
.GetText ("Invalid."));
79 if (access
< FileAccess
.Read
|| access
> FileAccess
.ReadWrite
)
80 throw new ArgumentOutOfRangeException ("access");
83 MonoFileType ftype
= MonoIO
.GetFileType (handle
, out error
);
85 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
86 throw MonoIO
.GetException (name
, error
);
89 if (ftype
== MonoFileType
.Unknown
) {
90 throw new IOException ("Invalid handle.");
91 } else if (ftype
== MonoFileType
.Disk
) {
99 this.owner
= ownsHandle
;
100 this.async = isAsync
;
102 // default the browser to 'all' anonymous files and let other usage (like smcs) with 'normal'
103 // (i.e. non-anonymous except for isolated storage) files and paths
104 this.anonymous
= SecurityManager
.SecurityEnabled
;
106 this.anonymous
= false;
111 InitBuffer (bufferSize
);
114 buf_start
= MonoIO
.Seek (handle
, 0, SeekOrigin
.Current
, out error
);
115 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
116 throw MonoIO
.GetException (name
, error
);
120 /* Can't set append mode */
121 this.append_startpos
=0;
124 // construct from filename
126 public FileStream (string path
, FileMode mode
)
127 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), FileShare
.Read
, DefaultBufferSize
, false, FileOptions
.None
)
131 public FileStream (string path
, FileMode mode
, FileAccess access
)
132 : this (path
, mode
, access
, access
== FileAccess
.Write
? FileShare
.None
: FileShare
.Read
, DefaultBufferSize
, false, false)
136 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
)
137 : this (path
, mode
, access
, share
, DefaultBufferSize
, false, FileOptions
.None
)
141 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
)
142 : this (path
, mode
, access
, share
, bufferSize
, false, FileOptions
.None
)
146 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool useAsync
)
147 : this (path
, mode
, access
, share
, bufferSize
, useAsync
? FileOptions
.Asynchronous
: FileOptions
.None
)
151 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, FileOptions options
)
152 : this (path
, mode
, access
, share
, bufferSize
, false, options
)
157 public FileStream (SafeFileHandle handle
, FileAccess access
)
158 :this(handle
, access
, DefaultBufferSize
, false)
162 public FileStream (SafeFileHandle handle
, FileAccess access
,
164 :this(handle
, access
, bufferSize
, false)
168 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
169 public FileStream (SafeFileHandle handle
, FileAccess access
,
170 int bufferSize
, bool isAsync
)
171 :this (handle
.DangerousGetHandle (), access
, false, bufferSize
, isAsync
)
173 this.safeHandle
= handle
;
176 [MonoLimitation ("This ignores the rights parameter")]
177 public FileStream (string path
, FileMode mode
,
178 FileSystemRights rights
, FileShare share
,
179 int bufferSize
, FileOptions options
)
180 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), share
, bufferSize
, false, options
)
184 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
185 public FileStream (string path
, FileMode mode
,
186 FileSystemRights rights
, FileShare share
,
187 int bufferSize
, FileOptions options
,
188 FileSecurity fileSecurity
)
189 : this (path
, mode
, (mode
== FileMode
.Append
? FileAccess
.Write
: FileAccess
.ReadWrite
), share
, bufferSize
, false, options
)
194 internal FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool isAsync
, bool anonymous
)
195 : this (path
, mode
, access
, share
, bufferSize
, anonymous
, isAsync
? FileOptions
.Asynchronous
: FileOptions
.None
)
199 internal FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, bool anonymous
, FileOptions options
)
202 throw new ArgumentNullException ("path");
205 if (path
.Length
== 0) {
206 throw new ArgumentException ("Path is empty");
209 // ignore the Inheritable flag
210 share
&= ~FileShare
.Inheritable
;
213 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
215 if (mode
< FileMode
.CreateNew
|| mode
> FileMode
.Append
) {
218 throw new ArgumentException ("mode", "Enum value was out of legal range.");
221 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
224 if (access
< FileAccess
.Read
|| access
> FileAccess
.ReadWrite
) {
227 throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range.");
230 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
233 if (share
< FileShare
.None
|| share
> (FileShare
.ReadWrite
| FileShare
.Delete
)) {
236 throw new IsolatedStorageException ("Enum value for FileShare was out of legal range.");
239 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
242 if (path
.IndexOfAny (Path
.InvalidPathChars
) != -1) {
243 throw new ArgumentException ("Name has invalid chars");
246 if (Directory
.Exists (path
)) {
247 // don't leak the path information for isolated storage
248 string msg
= Locale
.GetText ("Access to the path '{0}' is denied.");
249 throw new UnauthorizedAccessException (String
.Format (msg
, GetSecureFileName (path
, false)));
252 /* Append streams can't be read (see FileMode
255 if (mode
==FileMode
.Append
&&
256 (access
&FileAccess
.Read
)==FileAccess
.Read
) {
257 throw new ArgumentException("Append access can be requested only in write-only mode.");
260 if ((access
& FileAccess
.Write
) == 0 &&
261 (mode
!= FileMode
.Open
&& mode
!= FileMode
.OpenOrCreate
)) {
262 string msg
= Locale
.GetText ("Combining FileMode: {0} with " +
263 "FileAccess: {1} is invalid.");
264 throw new ArgumentException (string.Format (msg
, access
, mode
));
267 SecurityManager
.EnsureElevatedPermissions (); // this is a no-op outside moonlight
270 if (Path
.DirectorySeparatorChar
!= '/' && path
.IndexOf ('/') >= 0)
271 dname
= Path
.GetDirectoryName (Path
.GetFullPath (path
));
273 dname
= Path
.GetDirectoryName (path
);
274 if (dname
.Length
> 0) {
275 string fp
= Path
.GetFullPath (dname
);
276 if (!Directory
.Exists (fp
)) {
277 // don't leak the path information for isolated storage
278 string msg
= Locale
.GetText ("Could not find a part of the path \"{0}\".");
279 string fname
= (anonymous
) ? dname
: Path
.GetFullPath (path
);
281 // don't use GetSecureFileName for the directory name
282 throw new IsolatedStorageException (String
.Format (msg
, fname
));
284 throw new DirectoryNotFoundException (String
.Format (msg
, fname
));
289 if (access
== FileAccess
.Read
&& mode
!= FileMode
.Create
&& mode
!= FileMode
.OpenOrCreate
&&
290 mode
!= FileMode
.CreateNew
&& !File
.Exists (path
)) {
291 // don't leak the path information for isolated storage
292 string msg
= Locale
.GetText ("Could not find file \"{0}\".");
293 string fname
= GetSecureFileName (path
);
295 throw new IsolatedStorageException (String
.Format (msg
, fname
));
297 throw new FileNotFoundException (String
.Format (msg
, fname
), fname
);
301 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
305 // TODO: demand permissions
309 this.handle
= MonoIO
.Open (path
, mode
, access
, share
, options
, out error
);
310 if (handle
== MonoIO
.InvalidHandle
) {
311 // don't leak the path information for isolated storage
312 throw MonoIO
.GetException (GetSecureFileName (path
), error
);
315 this.access
= access
;
317 this.anonymous
= anonymous
;
319 /* Can we open non-files by name? */
321 if (MonoIO
.GetFileType (handle
, out error
) == MonoFileType
.Disk
) {
323 this.async = (options
& FileOptions
.Asynchronous
) != 0;
325 this.canseek
= false;
330 if (access
== FileAccess
.Read
&& canseek
&& (bufferSize
== DefaultBufferSize
)) {
331 /* Avoid allocating a large buffer for small files */
333 if (bufferSize
> len
) {
334 bufferSize
= (int)(len
< 1000 ? 1000 : len
);
338 InitBuffer (bufferSize
);
340 if (mode
==FileMode
.Append
) {
341 this.Seek (0, SeekOrigin
.End
);
342 this.append_startpos
=this.Position
;
344 this.append_startpos
=0;
350 public override bool CanRead
{
352 return access
== FileAccess
.Read
||
353 access
== FileAccess
.ReadWrite
;
357 public override bool CanWrite
{
359 return access
== FileAccess
.Write
||
360 access
== FileAccess
.ReadWrite
;
364 public override bool CanSeek
{
370 public virtual bool IsAsync
{
379 return SecurityManager
.CheckElevatedPermissions () ? name
: "[Unknown]";
386 public override long Length
{
388 if (handle
== MonoIO
.InvalidHandle
)
389 throw new ObjectDisposedException ("Stream has been closed");
392 throw new NotSupportedException ("The stream does not support seeking");
394 // Buffered data might change the length of the stream
395 FlushBufferIfDirty ();
400 length
= MonoIO
.GetLength (handle
, out error
);
401 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
402 // don't leak the path information for isolated storage
403 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
410 public override long Position
{
412 if (handle
== MonoIO
.InvalidHandle
)
413 throw new ObjectDisposedException ("Stream has been closed");
416 throw new NotSupportedException("The stream does not support seeking");
418 return(buf_start
+ buf_offset
);
421 if (handle
== MonoIO
.InvalidHandle
)
422 throw new ObjectDisposedException ("Stream has been closed");
424 if(CanSeek
== false) {
425 throw new NotSupportedException("The stream does not support seeking");
429 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
432 Seek (value, SeekOrigin
.Begin
);
436 [Obsolete ("Use SafeFileHandle instead")]
437 public virtual IntPtr Handle
{
438 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
439 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
445 public virtual SafeFileHandle SafeFileHandle
{
446 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
447 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
451 if (safeHandle
!= null)
454 ret
= new SafeFileHandle (handle
, owner
);
463 public override int ReadByte ()
465 if (handle
== MonoIO
.InvalidHandle
)
466 throw new ObjectDisposedException ("Stream has been closed");
469 throw new NotSupportedException ("Stream does not support reading");
472 int n
= ReadData (handle
, buf
, 0, 1);
473 if (n
== 0) return -1;
476 else if (buf_offset
>= buf_length
) {
483 return buf
[buf_offset
++];
486 public override void WriteByte (byte value)
488 if (handle
== MonoIO
.InvalidHandle
)
489 throw new ObjectDisposedException ("Stream has been closed");
492 throw new NotSupportedException ("Stream does not support writing");
494 if (buf_offset
== buf_size
)
497 if (buf_size
== 0) { // No buffering
505 buf
[buf_offset
++] = value;
506 if (buf_offset
> buf_length
)
507 buf_length
= buf_offset
;
512 public override int Read ([In
,Out
] byte[] array
, int offset
, int count
)
514 if (handle
== MonoIO
.InvalidHandle
)
515 throw new ObjectDisposedException ("Stream has been closed");
517 throw new ArgumentNullException ("array");
519 throw new NotSupportedException ("Stream does not support reading");
520 int len
= array
.Length
;
522 throw new ArgumentOutOfRangeException ("offset", "< 0");
524 throw new ArgumentOutOfRangeException ("count", "< 0");
526 throw new ArgumentException ("destination offset is beyond array size");
527 // reordered to avoid possible integer overflow
528 if (offset
> len
- count
)
529 throw new ArgumentException ("Reading would overrun buffer");
532 IAsyncResult ares
= BeginRead (array
, offset
, count
, null, null);
533 return EndRead (ares
);
536 return ReadInternal (array
, offset
, count
);
539 int ReadInternal (byte [] dest
, int offset
, int count
)
543 int n
= ReadSegment (dest
, offset
, count
);
548 /* If there was already enough
549 * buffered, no need to read
550 * more from the file.
555 if (count
> buf_size
) {
556 /* Read as much as we can, up
560 n
= ReadData (handle
, dest
,
564 /* Make the next buffer read
565 * start from the right place
570 n
= ReadSegment (dest
,
580 delegate int ReadDelegate (byte [] buffer
, int offset
, int count
);
582 public override IAsyncResult
BeginRead (byte [] array
, int offset
, int numBytes
,
583 AsyncCallback userCallback
, object stateObject
)
585 if (handle
== MonoIO
.InvalidHandle
)
586 throw new ObjectDisposedException ("Stream has been closed");
589 throw new NotSupportedException ("This stream does not support reading");
592 throw new ArgumentNullException ("array");
595 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
598 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
600 // reordered to avoid possible integer overflow
601 if (numBytes
> array
.Length
- offset
)
602 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
605 return base.BeginRead (array
, offset
, numBytes
, userCallback
, stateObject
);
607 ReadDelegate r
= new ReadDelegate (ReadInternal
);
608 return r
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
611 public override int EndRead (IAsyncResult asyncResult
)
613 if (asyncResult
== null)
614 throw new ArgumentNullException ("asyncResult");
617 return base.EndRead (asyncResult
);
619 AsyncResult ares
= asyncResult
as AsyncResult
;
621 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
623 ReadDelegate r
= ares
.AsyncDelegate
as ReadDelegate
;
625 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
627 return r
.EndInvoke (asyncResult
);
630 public override void Write (byte[] array
, int offset
, int count
)
632 if (handle
== MonoIO
.InvalidHandle
)
633 throw new ObjectDisposedException ("Stream has been closed");
635 throw new ArgumentNullException ("array");
637 throw new ArgumentOutOfRangeException ("offset", "< 0");
639 throw new ArgumentOutOfRangeException ("count", "< 0");
640 // ordered to avoid possible integer overflow
641 if (offset
> array
.Length
- count
)
642 throw new ArgumentException ("Reading would overrun buffer");
644 throw new NotSupportedException ("Stream does not support writing");
647 IAsyncResult ares
= BeginWrite (array
, offset
, count
, null, null);
652 WriteInternal (array
, offset
, count
);
655 void WriteInternal (byte [] src
, int offset
, int count
)
657 if (count
> buf_size
) {
658 // shortcut for long writes
665 int n
= MonoIO
.Write (handle
, src
, offset
, wcount
, out error
);
666 if (error
!= MonoIOError
.ERROR_SUCCESS
)
667 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
678 int n
= WriteSegment (src
, offset
+ copied
, count
);
691 delegate void WriteDelegate (byte [] buffer
, int offset
, int count
);
693 public override IAsyncResult
BeginWrite (byte [] array
, int offset
, int numBytes
,
694 AsyncCallback userCallback
, object stateObject
)
696 if (handle
== MonoIO
.InvalidHandle
)
697 throw new ObjectDisposedException ("Stream has been closed");
700 throw new NotSupportedException ("This stream does not support writing");
703 throw new ArgumentNullException ("array");
706 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
709 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
711 // reordered to avoid possible integer overflow
712 if (numBytes
> array
.Length
- offset
)
713 throw new ArgumentException ("array too small. numBytes/offset wrong.");
716 return base.BeginWrite (array
, offset
, numBytes
, userCallback
, stateObject
);
718 FileStreamAsyncResult result
= new FileStreamAsyncResult (userCallback
, stateObject
);
719 result
.BytesRead
= -1;
720 result
.Count
= numBytes
;
721 result
.OriginalCount
= numBytes
;
724 MemoryStream ms
= new MemoryStream ();
726 ms
.Write (array
, offset
, numBytes
);
728 numBytes
= (int) ms
.Length
;
731 WriteDelegate w
= new WriteDelegate (WriteInternal
);
732 return w
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
735 public override void EndWrite (IAsyncResult asyncResult
)
737 if (asyncResult
== null)
738 throw new ArgumentNullException ("asyncResult");
741 base.EndWrite (asyncResult
);
745 AsyncResult ares
= asyncResult
as AsyncResult
;
747 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
749 WriteDelegate w
= ares
.AsyncDelegate
as WriteDelegate
;
751 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
753 w
.EndInvoke (asyncResult
);
757 public override long Seek (long offset
, SeekOrigin origin
)
761 if (handle
== MonoIO
.InvalidHandle
)
762 throw new ObjectDisposedException ("Stream has been closed");
766 if(CanSeek
== false) {
767 throw new NotSupportedException("The stream does not support seeking");
772 pos
= Length
+ offset
;
775 case SeekOrigin
.Current
:
776 pos
= Position
+ offset
;
779 case SeekOrigin
.Begin
:
784 throw new ArgumentException ("origin", "Invalid SeekOrigin");
788 /* LAMESPEC: shouldn't this be
789 * ArgumentOutOfRangeException?
791 throw new IOException("Attempted to Seek before the beginning of the stream");
794 if(pos
< this.append_startpos
) {
795 /* More undocumented crap */
796 throw new IOException("Can't seek back over pre-existing data in append mode");
803 buf_start
= MonoIO
.Seek (handle
, pos
,
807 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
808 // don't leak the path information for isolated storage
809 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
815 public override void SetLength (long value)
817 if (handle
== MonoIO
.InvalidHandle
)
818 throw new ObjectDisposedException ("Stream has been closed");
821 throw new NotSupportedException("The stream does not support seeking");
823 if(CanWrite
== false)
824 throw new NotSupportedException("The stream does not support writing");
827 throw new ArgumentOutOfRangeException("value is less than 0");
833 MonoIO
.SetLength (handle
, value, out error
);
834 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
835 // don't leak the path information for isolated storage
836 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
839 if (Position
> value)
843 public override void Flush ()
845 if (handle
== MonoIO
.InvalidHandle
)
846 throw new ObjectDisposedException ("Stream has been closed");
852 public virtual void Flush (bool flushToDisk
)
856 // This does the fsync
859 MonoIO
.Flush (handle
, out error
);
864 public virtual void Lock (long position
, long length
)
866 if (handle
== MonoIO
.InvalidHandle
)
867 throw new ObjectDisposedException ("Stream has been closed");
869 throw new ArgumentOutOfRangeException ("position must not be negative");
872 throw new ArgumentOutOfRangeException ("length must not be negative");
874 if (handle
== MonoIO
.InvalidHandle
) {
875 throw new ObjectDisposedException ("Stream has been closed");
880 MonoIO
.Lock (handle
, position
, length
, out error
);
881 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
882 // don't leak the path information for isolated storage
883 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
887 public virtual void Unlock (long position
, long length
)
889 if (handle
== MonoIO
.InvalidHandle
)
890 throw new ObjectDisposedException ("Stream has been closed");
892 throw new ArgumentOutOfRangeException ("position must not be negative");
895 throw new ArgumentOutOfRangeException ("length must not be negative");
900 MonoIO
.Unlock (handle
, position
, length
, out error
);
901 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
902 // don't leak the path information for isolated storage
903 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
914 protected override void Dispose (bool disposing
)
916 Exception exc
= null;
917 if (handle
!= MonoIO
.InvalidHandle
) {
920 } catch (Exception e
) {
927 MonoIO
.Close (handle
, out error
);
928 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
929 // don't leak the path information for isolated storage
930 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
933 handle
= MonoIO
.InvalidHandle
;
940 if (disposing
&& buf
!= null) {
941 if (buf
.Length
== DefaultBufferSize
&& buf_recycle
== null) {
942 lock (buf_recycle_lock
) {
943 if (buf_recycle
== null) {
950 GC
.SuppressFinalize (this);
957 public FileSecurity
GetAccessControl ()
959 throw new NotImplementedException ();
962 public void SetAccessControl (FileSecurity fileSecurity
)
964 throw new NotImplementedException ();
970 // ReadSegment, WriteSegment, FlushBuffer,
971 // RefillBuffer and ReadData should only be called
972 // when the Monitor lock is held, but these methods
973 // grab it again just to be safe.
975 private int ReadSegment (byte [] dest
, int dest_offset
, int count
)
977 if (count
> buf_length
- buf_offset
) {
978 count
= buf_length
- buf_offset
;
982 Buffer
.BlockCopy (buf
, buf_offset
,
991 private int WriteSegment (byte [] src
, int src_offset
,
994 if (count
> buf_size
- buf_offset
) {
995 count
= buf_size
- buf_offset
;
999 Buffer
.BlockCopy (src
, src_offset
,
1002 buf_offset
+= count
;
1003 if (buf_offset
> buf_length
) {
1004 buf_length
= buf_offset
;
1013 void FlushBuffer (Stream st
)
1018 if (CanSeek
== true) {
1019 MonoIO
.Seek (handle
, buf_start
,
1022 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1023 // don't leak the path information for isolated storage
1024 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1028 int wcount
= buf_length
;
1031 int n
= MonoIO
.Write (handle
, buf
, 0, buf_length
, out error
);
1032 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1033 // don't leak the path information for isolated storage
1034 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1040 st
.Write (buf
, 0, buf_length
);
1044 buf_start
+= buf_offset
;
1045 buf_offset
= buf_length
= 0;
1049 private void FlushBuffer ()
1054 private void FlushBufferIfDirty ()
1060 private void RefillBuffer ()
1064 buf_length
= ReadData (handle
, buf
, 0,
1068 private int ReadData (IntPtr handle
, byte[] buf
, int offset
,
1074 /* when async == true, if we get here we don't suport AIO or it's disabled
1075 * and we're using the threadpool */
1076 amount
= MonoIO
.Read (handle
, buf
, offset
, count
, out error
);
1077 if (error
== MonoIOError
.ERROR_BROKEN_PIPE
) {
1078 amount
= 0; // might not be needed, but well...
1079 } else if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1080 // don't leak the path information for isolated storage
1081 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1084 /* Check for read error */
1086 throw new IOException ();
1092 void InitBuffer (int size
)
1095 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1097 size
= Math
.Max (size
, 8);
1100 // Instead of allocating a new default buffer use the
1101 // last one if there is any available
1103 if (size
<= DefaultBufferSize
&& buf_recycle
!= null) {
1104 lock (buf_recycle_lock
) {
1105 if (buf_recycle
!= null) {
1113 buf
= new byte [size
];
1115 Array
.Clear (buf
, 0, size
);
1119 // buf_offset = buf_length = 0;
1120 // buf_dirty = false;
1123 private string GetSecureFileName (string filename
)
1125 return (anonymous
) ? Path
.GetFileName (filename
) : Path
.GetFullPath (filename
);
1128 private string GetSecureFileName (string filename
, bool full
)
1130 return (anonymous
) ? Path
.GetFileName (filename
) :
1131 (full
) ? Path
.GetFullPath (filename
) : filename
;
1136 internal const int DefaultBufferSize
= 8192;
1138 // Input buffer ready for recycling
1139 static byte[] buf_recycle
;
1140 static readonly object buf_recycle_lock
= new object ();
1142 private FileAccess access
;
1145 private bool canseek
;
1146 private long append_startpos
;
1147 private bool anonymous
;
1149 private byte [] buf
; // the buffer
1150 private int buf_size
; // capacity in bytes
1151 private int buf_length
; // number of valid bytes in buffer
1152 private int buf_offset
; // position of next byte
1153 private bool buf_dirty
; // true if buffer has been written to
1154 private long buf_start
; // location of buffer in file
1155 private string name
= "[Unknown]"; // name of file.
1157 IntPtr handle
; // handle to underlying file
1158 SafeFileHandle safeHandle
; // set only when using one of the
1159 // constructors taking SafeFileHandle