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 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
.Permissions
;
39 using System
.Threading
;
41 using Microsoft
.Win32
.SafeHandles
;
43 using System
.IO
.IsolatedStorage
;
44 using System
.Security
;
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
.None
)
152 public FileStream (string path
, FileMode mode
, FileAccess access
, FileShare share
, int bufferSize
, FileOptions options
)
153 : 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 string dname
= Path
.GetDirectoryName (path
);
268 if (dname
.Length
> 0) {
269 string fp
= Path
.GetFullPath (dname
);
270 if (!Directory
.Exists (fp
)) {
271 // don't leak the path information for isolated storage
272 string msg
= Locale
.GetText ("Could not find a part of the path \"{0}\".");
273 string fname
= (anonymous
) ? dname
: Path
.GetFullPath (path
);
275 // don't use GetSecureFileName for the directory name
276 throw new IsolatedStorageException (String
.Format (msg
, fname
));
278 throw new DirectoryNotFoundException (String
.Format (msg
, fname
));
283 if (access
== FileAccess
.Read
&& mode
!= FileMode
.Create
&& mode
!= FileMode
.OpenOrCreate
&&
284 mode
!= FileMode
.CreateNew
&& !File
.Exists (path
)) {
285 // don't leak the path information for isolated storage
286 string msg
= Locale
.GetText ("Could not find file \"{0}\".");
287 string fname
= GetSecureFileName (path
);
289 throw new IsolatedStorageException (String
.Format (msg
, fname
));
291 throw new FileNotFoundException (String
.Format (msg
, fname
), fname
);
295 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
299 // TODO: demand permissions
303 this.handle
= MonoIO
.Open (path
, mode
, access
, share
, options
, out error
);
304 if (handle
== MonoIO
.InvalidHandle
) {
305 // don't leak the path information for isolated storage
306 throw MonoIO
.GetException (GetSecureFileName (path
), error
);
309 this.access
= access
;
311 this.anonymous
= anonymous
;
313 /* Can we open non-files by name? */
315 if (MonoIO
.GetFileType (handle
, out error
) == MonoFileType
.Disk
) {
317 this.async = (options
& FileOptions
.Asynchronous
) != 0;
319 this.canseek
= false;
324 if (access
== FileAccess
.Read
&& canseek
&& (bufferSize
== DefaultBufferSize
)) {
325 /* Avoid allocating a large buffer for small files */
327 if (bufferSize
> len
) {
328 bufferSize
= (int)(len
< 1000 ? 1000 : len
);
332 InitBuffer (bufferSize
);
334 if (mode
==FileMode
.Append
) {
335 this.Seek (0, SeekOrigin
.End
);
336 this.append_startpos
=this.Position
;
338 this.append_startpos
=0;
344 public override bool CanRead
{
346 return access
== FileAccess
.Read
||
347 access
== FileAccess
.ReadWrite
;
351 public override bool CanWrite
{
353 return access
== FileAccess
.Write
||
354 access
== FileAccess
.ReadWrite
;
358 public override bool CanSeek
{
364 public virtual bool IsAsync
{
376 public override long Length
{
378 if (handle
== MonoIO
.InvalidHandle
)
379 throw new ObjectDisposedException ("Stream has been closed");
382 throw new NotSupportedException ("The stream does not support seeking");
384 // Buffered data might change the length of the stream
385 FlushBufferIfDirty ();
390 length
= MonoIO
.GetLength (handle
, out error
);
391 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
392 // don't leak the path information for isolated storage
393 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
400 public override long Position
{
402 if (handle
== MonoIO
.InvalidHandle
)
403 throw new ObjectDisposedException ("Stream has been closed");
406 throw new NotSupportedException("The stream does not support seeking");
408 return(buf_start
+ buf_offset
);
411 if (handle
== MonoIO
.InvalidHandle
)
412 throw new ObjectDisposedException ("Stream has been closed");
414 if(CanSeek
== false) {
415 throw new NotSupportedException("The stream does not support seeking");
419 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
422 Seek (value, SeekOrigin
.Begin
);
426 [Obsolete ("Use SafeFileHandle instead")]
427 public virtual IntPtr Handle
{
428 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
429 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
435 public virtual SafeFileHandle SafeFileHandle
{
436 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
437 [SecurityPermission (SecurityAction
.InheritanceDemand
, UnmanagedCode
= true)]
441 if (safeHandle
!= null)
444 ret
= new SafeFileHandle (handle
, owner
);
453 public override int ReadByte ()
455 if (handle
== MonoIO
.InvalidHandle
)
456 throw new ObjectDisposedException ("Stream has been closed");
459 throw new NotSupportedException ("Stream does not support reading");
462 int n
= ReadData (handle
, buf
, 0, 1);
463 if (n
== 0) return -1;
466 else if (buf_offset
>= buf_length
) {
473 return buf
[buf_offset
++];
476 public override void WriteByte (byte value)
478 if (handle
== MonoIO
.InvalidHandle
)
479 throw new ObjectDisposedException ("Stream has been closed");
482 throw new NotSupportedException ("Stream does not support writing");
484 if (buf_offset
== buf_size
)
487 if (buf_size
== 0) { // No buffering
495 buf
[buf_offset
++] = value;
496 if (buf_offset
> buf_length
)
497 buf_length
= buf_offset
;
502 public override int Read ([In
,Out
] byte[] array
, int offset
, int count
)
504 if (handle
== MonoIO
.InvalidHandle
)
505 throw new ObjectDisposedException ("Stream has been closed");
507 throw new ArgumentNullException ("array");
509 throw new NotSupportedException ("Stream does not support reading");
510 int len
= array
.Length
;
512 throw new ArgumentOutOfRangeException ("offset", "< 0");
514 throw new ArgumentOutOfRangeException ("count", "< 0");
516 throw new ArgumentException ("destination offset is beyond array size");
517 // reordered to avoid possible integer overflow
518 if (offset
> len
- count
)
519 throw new ArgumentException ("Reading would overrun buffer");
522 IAsyncResult ares
= BeginRead (array
, offset
, count
, null, null);
523 return EndRead (ares
);
526 return ReadInternal (array
, offset
, count
);
529 int ReadInternal (byte [] dest
, int offset
, int count
)
533 int n
= ReadSegment (dest
, offset
, count
);
538 /* If there was already enough
539 * buffered, no need to read
540 * more from the file.
545 if (count
> buf_size
) {
546 /* Read as much as we can, up
550 n
= ReadData (handle
, dest
,
554 /* Make the next buffer read
555 * start from the right place
560 n
= ReadSegment (dest
,
570 delegate int ReadDelegate (byte [] buffer
, int offset
, int count
);
572 public override IAsyncResult
BeginRead (byte [] array
, int offset
, int numBytes
,
573 AsyncCallback userCallback
, object stateObject
)
575 if (handle
== MonoIO
.InvalidHandle
)
576 throw new ObjectDisposedException ("Stream has been closed");
579 throw new NotSupportedException ("This stream does not support reading");
582 throw new ArgumentNullException ("array");
585 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
588 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
590 // reordered to avoid possible integer overflow
591 if (numBytes
> array
.Length
- offset
)
592 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
595 return base.BeginRead (array
, offset
, numBytes
, userCallback
, stateObject
);
597 ReadDelegate r
= new ReadDelegate (ReadInternal
);
598 return r
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
601 public override int EndRead (IAsyncResult asyncResult
)
603 if (asyncResult
== null)
604 throw new ArgumentNullException ("asyncResult");
607 return base.EndRead (asyncResult
);
609 AsyncResult ares
= asyncResult
as AsyncResult
;
611 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
613 ReadDelegate r
= ares
.AsyncDelegate
as ReadDelegate
;
615 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
617 return r
.EndInvoke (asyncResult
);
620 public override void Write (byte[] array
, int offset
, int count
)
622 if (handle
== MonoIO
.InvalidHandle
)
623 throw new ObjectDisposedException ("Stream has been closed");
625 throw new ArgumentNullException ("array");
627 throw new ArgumentOutOfRangeException ("offset", "< 0");
629 throw new ArgumentOutOfRangeException ("count", "< 0");
630 // ordered to avoid possible integer overflow
631 if (offset
> array
.Length
- count
)
632 throw new ArgumentException ("Reading would overrun buffer");
634 throw new NotSupportedException ("Stream does not support writing");
637 IAsyncResult ares
= BeginWrite (array
, offset
, count
, null, null);
642 WriteInternal (array
, offset
, count
);
645 void WriteInternal (byte [] src
, int offset
, int count
)
647 if (count
> buf_size
) {
648 // shortcut for long writes
655 int n
= MonoIO
.Write (handle
, src
, offset
, wcount
, out error
);
656 if (error
!= MonoIOError
.ERROR_SUCCESS
)
657 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
668 int n
= WriteSegment (src
, offset
+ copied
, count
);
681 delegate void WriteDelegate (byte [] buffer
, int offset
, int count
);
683 public override IAsyncResult
BeginWrite (byte [] array
, int offset
, int numBytes
,
684 AsyncCallback userCallback
, object stateObject
)
686 if (handle
== MonoIO
.InvalidHandle
)
687 throw new ObjectDisposedException ("Stream has been closed");
690 throw new NotSupportedException ("This stream does not support writing");
693 throw new ArgumentNullException ("array");
696 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
699 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
701 // reordered to avoid possible integer overflow
702 if (numBytes
> array
.Length
- offset
)
703 throw new ArgumentException ("array too small. numBytes/offset wrong.");
706 return base.BeginWrite (array
, offset
, numBytes
, userCallback
, stateObject
);
708 FileStreamAsyncResult result
= new FileStreamAsyncResult (userCallback
, stateObject
);
709 result
.BytesRead
= -1;
710 result
.Count
= numBytes
;
711 result
.OriginalCount
= numBytes
;
714 MemoryStream ms
= new MemoryStream ();
716 ms
.Write (array
, offset
, numBytes
);
718 numBytes
= (int) ms
.Length
;
721 WriteDelegate w
= new WriteDelegate (WriteInternal
);
722 return w
.BeginInvoke (array
, offset
, numBytes
, userCallback
, stateObject
);
725 public override void EndWrite (IAsyncResult asyncResult
)
727 if (asyncResult
== null)
728 throw new ArgumentNullException ("asyncResult");
731 base.EndWrite (asyncResult
);
735 AsyncResult ares
= asyncResult
as AsyncResult
;
737 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
739 WriteDelegate w
= ares
.AsyncDelegate
as WriteDelegate
;
741 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
743 w
.EndInvoke (asyncResult
);
747 public override long Seek (long offset
, SeekOrigin origin
)
751 if (handle
== MonoIO
.InvalidHandle
)
752 throw new ObjectDisposedException ("Stream has been closed");
756 if(CanSeek
== false) {
757 throw new NotSupportedException("The stream does not support seeking");
762 pos
= Length
+ offset
;
765 case SeekOrigin
.Current
:
766 pos
= Position
+ offset
;
769 case SeekOrigin
.Begin
:
774 throw new ArgumentException ("origin", "Invalid SeekOrigin");
778 /* LAMESPEC: shouldn't this be
779 * ArgumentOutOfRangeException?
781 throw new IOException("Attempted to Seek before the beginning of the stream");
784 if(pos
< this.append_startpos
) {
785 /* More undocumented crap */
786 throw new IOException("Can't seek back over pre-existing data in append mode");
793 buf_start
= MonoIO
.Seek (handle
, pos
,
797 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
798 // don't leak the path information for isolated storage
799 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
805 public override void SetLength (long value)
807 if (handle
== MonoIO
.InvalidHandle
)
808 throw new ObjectDisposedException ("Stream has been closed");
811 throw new NotSupportedException("The stream does not support seeking");
813 if(CanWrite
== false)
814 throw new NotSupportedException("The stream does not support writing");
817 throw new ArgumentOutOfRangeException("value is less than 0");
823 MonoIO
.SetLength (handle
, value, out error
);
824 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
825 // don't leak the path information for isolated storage
826 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
829 if (Position
> value)
833 public override void Flush ()
835 if (handle
== MonoIO
.InvalidHandle
)
836 throw new ObjectDisposedException ("Stream has been closed");
842 public virtual void Flush (bool flushToDisk
)
846 // This does the fsync
849 MonoIO
.Flush (handle
, out error
);
854 public virtual void Lock (long position
, long length
)
856 if (handle
== MonoIO
.InvalidHandle
)
857 throw new ObjectDisposedException ("Stream has been closed");
859 throw new ArgumentOutOfRangeException ("position must not be negative");
862 throw new ArgumentOutOfRangeException ("length must not be negative");
864 if (handle
== MonoIO
.InvalidHandle
) {
865 throw new ObjectDisposedException ("Stream has been closed");
870 MonoIO
.Lock (handle
, position
, length
, out error
);
871 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
872 // don't leak the path information for isolated storage
873 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
877 public virtual void Unlock (long position
, long length
)
879 if (handle
== MonoIO
.InvalidHandle
)
880 throw new ObjectDisposedException ("Stream has been closed");
882 throw new ArgumentOutOfRangeException ("position must not be negative");
885 throw new ArgumentOutOfRangeException ("length must not be negative");
890 MonoIO
.Unlock (handle
, position
, length
, out error
);
891 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
892 // don't leak the path information for isolated storage
893 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
904 protected override void Dispose (bool disposing
)
906 Exception exc
= null;
907 if (handle
!= MonoIO
.InvalidHandle
) {
910 } catch (Exception e
) {
917 MonoIO
.Close (handle
, out error
);
918 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
919 // don't leak the path information for isolated storage
920 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
923 handle
= MonoIO
.InvalidHandle
;
930 if (disposing
&& buf
!= null) {
931 if (buf
.Length
== DefaultBufferSize
&& buf_recycle
== null) {
932 lock (buf_recycle_lock
) {
933 if (buf_recycle
== null) {
940 GC
.SuppressFinalize (this);
947 public FileSecurity
GetAccessControl ()
949 throw new NotImplementedException ();
952 public void SetAccessControl (FileSecurity fileSecurity
)
954 throw new NotImplementedException ();
960 // ReadSegment, WriteSegment, FlushBuffer,
961 // RefillBuffer and ReadData should only be called
962 // when the Monitor lock is held, but these methods
963 // grab it again just to be safe.
965 private int ReadSegment (byte [] dest
, int dest_offset
, int count
)
967 if (count
> buf_length
- buf_offset
) {
968 count
= buf_length
- buf_offset
;
972 Buffer
.BlockCopy (buf
, buf_offset
,
981 private int WriteSegment (byte [] src
, int src_offset
,
984 if (count
> buf_size
- buf_offset
) {
985 count
= buf_size
- buf_offset
;
989 Buffer
.BlockCopy (src
, src_offset
,
993 if (buf_offset
> buf_length
) {
994 buf_length
= buf_offset
;
1003 void FlushBuffer (Stream st
)
1008 if (CanSeek
== true) {
1009 MonoIO
.Seek (handle
, buf_start
,
1012 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1013 // don't leak the path information for isolated storage
1014 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1018 int wcount
= buf_length
;
1021 int n
= MonoIO
.Write (handle
, buf
, 0, buf_length
, out error
);
1022 if (error
!= MonoIOError
.ERROR_SUCCESS
) {
1023 // don't leak the path information for isolated storage
1024 throw MonoIO
.GetException (GetSecureFileName (name
), error
);
1030 st
.Write (buf
, 0, buf_length
);
1034 buf_start
+= buf_offset
;
1035 buf_offset
= buf_length
= 0;
1039 private void FlushBuffer ()
1044 private void FlushBufferIfDirty ()
1050 private void RefillBuffer ()
1054 buf_length
= ReadData (handle
, buf
, 0,
1058 private int ReadData (IntPtr handle
, 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 (handle
, 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
)
1085 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1087 size
= Math
.Max (size
, 8);
1090 // Instead of allocating a new default buffer use the
1091 // last one if there is any available
1093 if (size
<= DefaultBufferSize
&& buf_recycle
!= null) {
1094 lock (buf_recycle_lock
) {
1095 if (buf_recycle
!= null) {
1103 buf
= new byte [size
];
1105 Array
.Clear (buf
, 0, size
);
1109 // buf_offset = buf_length = 0;
1110 // buf_dirty = false;
1113 private string GetSecureFileName (string filename
)
1115 return (anonymous
) ? Path
.GetFileName (filename
) : Path
.GetFullPath (filename
);
1118 private string GetSecureFileName (string filename
, bool full
)
1120 return (anonymous
) ? Path
.GetFileName (filename
) :
1121 (full
) ? Path
.GetFullPath (filename
) : filename
;
1126 internal const int DefaultBufferSize
= 8192;
1128 // Input buffer ready for recycling
1129 static byte[] buf_recycle
;
1130 static readonly object buf_recycle_lock
= new object ();
1132 private FileAccess access
;
1135 private bool canseek
;
1136 private long append_startpos
;
1137 private bool anonymous
;
1139 private byte [] buf
; // the buffer
1140 private int buf_size
; // capacity in bytes
1141 private int buf_length
; // number of valid bytes in buffer
1142 private int buf_offset
; // position of next byte
1143 private bool buf_dirty
; // true if buffer has been written to
1144 private long buf_start
; // location of buffer in file
1145 private string name
= "[Unknown]"; // name of file.
1147 IntPtr handle
; // handle to underlying file
1148 SafeFileHandle safeHandle
; // set only when using one of the
1149 // constructors taking SafeFileHandle