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.Permissions;
41 using System.Threading;
42 using Microsoft.Win32.SafeHandles;
45 using System.IO.IsolatedStorage;
47 using System.Security.AccessControl;
51 using System.Threading.Tasks;
57 public class FileStream : Stream
59 // construct from handle
61 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
62 public FileStream (IntPtr handle, FileAccess access)
63 : this (handle, access, true, DefaultBufferSize, false) {}
65 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
66 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
67 : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
69 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
70 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
71 : this (handle, access, ownsHandle, bufferSize, false) {}
73 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
74 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
75 : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
77 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
78 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isZeroSize)
80 this.handle = MonoIO.InvalidHandle;
81 if (handle == this.handle)
82 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
84 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
85 throw new ArgumentOutOfRangeException ("access");
88 MonoFileType ftype = MonoIO.GetFileType (handle, out error);
90 if (error != MonoIOError.ERROR_SUCCESS) {
91 throw MonoIO.GetException (name, error);
94 if (ftype == MonoFileType.Unknown) {
95 throw new IOException ("Invalid handle.");
96 } else if (ftype == MonoFileType.Disk) {
102 this.handle = handle;
104 this.access = access;
105 this.owner = ownsHandle;
106 this.async = isAsync;
107 this.anonymous = false;
109 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
110 if (error != MonoIOError.ERROR_SUCCESS) {
111 throw MonoIO.GetException (name, error);
115 /* Can't set append mode */
116 this.append_startpos=0;
119 // construct from filename
121 public FileStream (string path, FileMode mode)
122 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
126 public FileStream (string path, FileMode mode, FileAccess access)
127 : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
131 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
132 : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
136 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
137 : this (path, mode, access, share, bufferSize, false, FileOptions.None)
141 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
142 : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
146 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
147 : this (path, mode, access, share, bufferSize, false, options)
152 public FileStream (SafeFileHandle handle, FileAccess access)
153 :this(handle, access, DefaultBufferSize, false)
157 public FileStream (SafeFileHandle handle, FileAccess access,
159 :this(handle, access, bufferSize, false)
163 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
164 public FileStream (SafeFileHandle handle, FileAccess access,
165 int bufferSize, bool isAsync)
166 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
168 this.safeHandle = handle;
171 [MonoLimitation ("This ignores the rights parameter")]
172 public FileStream (string path, FileMode mode,
173 FileSystemRights rights, FileShare share,
174 int bufferSize, FileOptions options)
175 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
179 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
180 public FileStream (string path, FileMode mode,
181 FileSystemRights rights, FileShare share,
182 int bufferSize, FileOptions options,
183 FileSecurity fileSecurity)
184 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
189 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
190 : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
194 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
197 throw new ArgumentNullException ("path");
200 if (path.Length == 0) {
201 throw new ArgumentException ("Path is empty");
204 this.anonymous = anonymous;
205 // ignore the Inheritable flag
206 share &= ~FileShare.Inheritable;
209 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
211 if (mode < FileMode.CreateNew || mode > FileMode.Append) {
214 throw new ArgumentException ("mode", "Enum value was out of legal range.");
217 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
220 if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
221 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
224 if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
225 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
228 if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
229 throw new ArgumentException ("Name has invalid chars");
232 if (Directory.Exists (path)) {
233 // don't leak the path information for isolated storage
234 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
235 throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
238 /* Append streams can't be read (see FileMode
241 if (mode==FileMode.Append &&
242 (access&FileAccess.Read)==FileAccess.Read) {
243 throw new ArgumentException("Append access can be requested only in write-only mode.");
246 if ((access & FileAccess.Write) == 0 &&
247 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
248 string msg = Locale.GetText ("Combining FileMode: {0} with " +
249 "FileAccess: {1} is invalid.");
250 throw new ArgumentException (string.Format (msg, access, mode));
253 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
256 if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
257 dname = Path.GetDirectoryName (Path.GetFullPath (path));
259 dname = Path.GetDirectoryName (path);
260 if (dname.Length > 0) {
261 string fp = Path.GetFullPath (dname);
262 if (!Directory.Exists (fp)) {
263 // don't leak the path information for isolated storage
264 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
265 string fname = (anonymous) ? dname : Path.GetFullPath (path);
266 throw new DirectoryNotFoundException (String.Format (msg, fname));
270 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
271 mode != FileMode.CreateNew && !File.Exists (path)) {
272 // don't leak the path information for isolated storage
273 string msg = Locale.GetText ("Could not find file \"{0}\".");
274 string fname = GetSecureFileName (path);
275 throw new FileNotFoundException (String.Format (msg, fname), fname);
278 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
282 // TODO: demand permissions
286 this.handle = MonoIO.Open (path, mode, access, share, options, out error);
287 if (handle == MonoIO.InvalidHandle) {
288 // don't leak the path information for isolated storage
289 throw MonoIO.GetException (GetSecureFileName (path), error);
292 this.access = access;
295 /* Can we open non-files by name? */
297 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
299 this.async = (options & FileOptions.Asynchronous) != 0;
301 this.canseek = false;
306 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
307 /* Avoid allocating a large buffer for small files */
309 if (bufferSize > len) {
310 bufferSize = (int)(len < 1000 ? 1000 : len);
314 InitBuffer (bufferSize, false);
316 if (mode==FileMode.Append) {
317 this.Seek (0, SeekOrigin.End);
318 this.append_startpos=this.Position;
320 this.append_startpos=0;
326 public override bool CanRead {
328 return access == FileAccess.Read ||
329 access == FileAccess.ReadWrite;
333 public override bool CanWrite {
335 return access == FileAccess.Write ||
336 access == FileAccess.ReadWrite;
340 public override bool CanSeek {
346 public virtual bool IsAsync {
358 public override long Length {
360 if (handle == MonoIO.InvalidHandle)
361 throw new ObjectDisposedException ("Stream has been closed");
364 throw new NotSupportedException ("The stream does not support seeking");
366 // Buffered data might change the length of the stream
367 FlushBufferIfDirty ();
372 length = MonoIO.GetLength (handle, out error);
373 if (error != MonoIOError.ERROR_SUCCESS) {
374 // don't leak the path information for isolated storage
375 throw MonoIO.GetException (GetSecureFileName (name), error);
382 public override long Position {
384 if (handle == MonoIO.InvalidHandle)
385 throw new ObjectDisposedException ("Stream has been closed");
387 if (CanSeek == false)
388 throw new NotSupportedException("The stream does not support seeking");
390 if (safeHandle != null) {
391 // If the handle was leaked outside we always ask the real handle
394 long ret = MonoIO.Seek (handle, 0,SeekOrigin.Current,out error);
396 if (error != MonoIOError.ERROR_SUCCESS) {
397 // don't leak the path information for isolated storage
398 throw MonoIO.GetException (GetSecureFileName (name), error);
404 return(buf_start + buf_offset);
408 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
411 Seek (value, SeekOrigin.Begin);
415 [Obsolete ("Use SafeFileHandle instead")]
416 public virtual IntPtr Handle {
417 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
418 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
420 if (safeHandle == null) {
427 public virtual SafeFileHandle SafeFileHandle {
428 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
429 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
431 if (safeHandle == null) {
442 safeHandle = new SafeFileHandle (handle, false);
444 InitBuffer (0, true);
447 public override int ReadByte ()
449 if (handle == MonoIO.InvalidHandle)
450 throw new ObjectDisposedException ("Stream has been closed");
453 throw new NotSupportedException ("Stream does not support reading");
456 int n = ReadData (handle, buf, 0, 1);
457 if (n == 0) return -1;
460 else if (buf_offset >= buf_length) {
467 return buf [buf_offset ++];
470 public override void WriteByte (byte value)
472 if (handle == MonoIO.InvalidHandle)
473 throw new ObjectDisposedException ("Stream has been closed");
476 throw new NotSupportedException ("Stream does not support writing");
478 if (buf_offset == buf_size)
481 if (buf_size == 0) { // No buffering
489 buf [buf_offset ++] = value;
490 if (buf_offset > buf_length)
491 buf_length = buf_offset;
496 public override int Read ([In,Out] byte[] array, int offset, int count)
498 if (handle == MonoIO.InvalidHandle)
499 throw new ObjectDisposedException ("Stream has been closed");
501 throw new ArgumentNullException ("array");
503 throw new NotSupportedException ("Stream does not support reading");
504 int len = array.Length;
506 throw new ArgumentOutOfRangeException ("offset", "< 0");
508 throw new ArgumentOutOfRangeException ("count", "< 0");
510 throw new ArgumentException ("destination offset is beyond array size");
511 // reordered to avoid possible integer overflow
512 if (offset > len - count)
513 throw new ArgumentException ("Reading would overrun buffer");
516 IAsyncResult ares = BeginRead (array, offset, count, null, null);
517 return EndRead (ares);
520 return ReadInternal (array, offset, count);
523 int ReadInternal (byte [] dest, int offset, int count)
525 int n = ReadSegment (dest, offset, count);
532 if (count > buf_size) {
533 /* Read as much as we can, up
537 n = ReadData (handle, dest,
541 /* Make the next buffer read
542 * start from the right place
547 n = ReadSegment (dest,
555 delegate int ReadDelegate (byte [] buffer, int offset, int count);
557 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
558 AsyncCallback userCallback, object stateObject)
560 if (handle == MonoIO.InvalidHandle)
561 throw new ObjectDisposedException ("Stream has been closed");
564 throw new NotSupportedException ("This stream does not support reading");
567 throw new ArgumentNullException ("array");
570 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
573 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
575 // reordered to avoid possible integer overflow
576 if (numBytes > array.Length - offset)
577 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
580 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
582 ReadDelegate r = new ReadDelegate (ReadInternal);
583 return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
586 public override int EndRead (IAsyncResult asyncResult)
588 if (asyncResult == null)
589 throw new ArgumentNullException ("asyncResult");
592 return base.EndRead (asyncResult);
594 AsyncResult ares = asyncResult as AsyncResult;
596 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
598 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
600 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
602 return r.EndInvoke (asyncResult);
605 public override void Write (byte[] array, int offset, int count)
607 if (handle == MonoIO.InvalidHandle)
608 throw new ObjectDisposedException ("Stream has been closed");
610 throw new ArgumentNullException ("array");
612 throw new ArgumentOutOfRangeException ("offset", "< 0");
614 throw new ArgumentOutOfRangeException ("count", "< 0");
615 // ordered to avoid possible integer overflow
616 if (offset > array.Length - count)
617 throw new ArgumentException ("Reading would overrun buffer");
619 throw new NotSupportedException ("Stream does not support writing");
622 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
627 WriteInternal (array, offset, count);
630 void WriteInternal (byte [] src, int offset, int count)
632 if (count > buf_size) {
633 // shortcut for long writes
640 int n = MonoIO.Write (handle, src, offset, wcount, out error);
641 if (error != MonoIOError.ERROR_SUCCESS)
642 throw MonoIO.GetException (GetSecureFileName (name), error);
653 int n = WriteSegment (src, offset + copied, count);
666 delegate void WriteDelegate (byte [] buffer, int offset, int count);
668 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
669 AsyncCallback userCallback, object stateObject)
671 if (handle == MonoIO.InvalidHandle)
672 throw new ObjectDisposedException ("Stream has been closed");
675 throw new NotSupportedException ("This stream does not support writing");
678 throw new ArgumentNullException ("array");
681 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
684 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
686 // reordered to avoid possible integer overflow
687 if (numBytes > array.Length - offset)
688 throw new ArgumentException ("array too small. numBytes/offset wrong.");
691 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
693 FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
694 result.BytesRead = -1;
695 result.Count = numBytes;
696 result.OriginalCount = numBytes;
699 MemoryStream ms = new MemoryStream ();
701 ms.Write (array, offset, numBytes);
703 // Set arguments to new compounded buffer
705 array = ms.ToArray ();
706 numBytes = array.Length;
709 WriteDelegate w = WriteInternal;
710 return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
713 public override void EndWrite (IAsyncResult asyncResult)
715 if (asyncResult == null)
716 throw new ArgumentNullException ("asyncResult");
719 base.EndWrite (asyncResult);
723 AsyncResult ares = asyncResult as AsyncResult;
725 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
727 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
729 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
731 w.EndInvoke (asyncResult);
735 public override long Seek (long offset, SeekOrigin origin)
739 if (handle == MonoIO.InvalidHandle)
740 throw new ObjectDisposedException ("Stream has been closed");
744 if(CanSeek == false) {
745 throw new NotSupportedException("The stream does not support seeking");
750 pos = Length + offset;
753 case SeekOrigin.Current:
754 pos = Position + offset;
757 case SeekOrigin.Begin:
762 throw new ArgumentException ("origin", "Invalid SeekOrigin");
766 /* LAMESPEC: shouldn't this be
767 * ArgumentOutOfRangeException?
769 throw new IOException("Attempted to Seek before the beginning of the stream");
772 if(pos < this.append_startpos) {
773 /* More undocumented crap */
774 throw new IOException("Can't seek back over pre-existing data in append mode");
781 buf_start = MonoIO.Seek (handle, pos,
785 if (error != MonoIOError.ERROR_SUCCESS) {
786 // don't leak the path information for isolated storage
787 throw MonoIO.GetException (GetSecureFileName (name), error);
793 public override void SetLength (long value)
795 if (handle == MonoIO.InvalidHandle)
796 throw new ObjectDisposedException ("Stream has been closed");
799 throw new NotSupportedException("The stream does not support seeking");
801 if(CanWrite == false)
802 throw new NotSupportedException("The stream does not support writing");
805 throw new ArgumentOutOfRangeException("value is less than 0");
811 MonoIO.SetLength (handle, value, out error);
812 if (error != MonoIOError.ERROR_SUCCESS) {
813 // don't leak the path information for isolated storage
814 throw MonoIO.GetException (GetSecureFileName (name), error);
817 if (Position > value)
821 public override void Flush ()
823 if (handle == MonoIO.InvalidHandle)
824 throw new ObjectDisposedException ("Stream has been closed");
830 public virtual void Flush (bool flushToDisk)
834 // This does the fsync
837 MonoIO.Flush (handle, out error);
842 public virtual void Lock (long position, long length)
844 if (handle == MonoIO.InvalidHandle)
845 throw new ObjectDisposedException ("Stream has been closed");
847 throw new ArgumentOutOfRangeException ("position must not be negative");
850 throw new ArgumentOutOfRangeException ("length must not be negative");
852 if (handle == MonoIO.InvalidHandle) {
853 throw new ObjectDisposedException ("Stream has been closed");
858 MonoIO.Lock (handle, position, length, out error);
859 if (error != MonoIOError.ERROR_SUCCESS) {
860 // don't leak the path information for isolated storage
861 throw MonoIO.GetException (GetSecureFileName (name), error);
865 public virtual void Unlock (long position, long length)
867 if (handle == MonoIO.InvalidHandle)
868 throw new ObjectDisposedException ("Stream has been closed");
870 throw new ArgumentOutOfRangeException ("position must not be negative");
873 throw new ArgumentOutOfRangeException ("length must not be negative");
878 MonoIO.Unlock (handle, position, length, out error);
879 if (error != MonoIOError.ERROR_SUCCESS) {
880 // don't leak the path information for isolated storage
881 throw MonoIO.GetException (GetSecureFileName (name), error);
892 protected override void Dispose (bool disposing)
894 Exception exc = null;
895 if (handle != MonoIO.InvalidHandle) {
897 // If the FileStream is in "exposed" status
898 // it means that we do not have a buffer(we write the data without buffering)
899 // therefor we don't and can't flush the buffer becouse we don't have one.
901 } catch (Exception e) {
908 MonoIO.Close (handle, out error);
909 if (error != MonoIOError.ERROR_SUCCESS) {
910 // don't leak the path information for isolated storage
911 throw MonoIO.GetException (GetSecureFileName (name), error);
914 handle = MonoIO.InvalidHandle;
921 if (disposing && buf != null) {
922 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
923 lock (buf_recycle_lock) {
924 if (buf_recycle == null) {
931 GC.SuppressFinalize (this);
938 public FileSecurity GetAccessControl ()
940 return new FileSecurity (SafeFileHandle,
941 AccessControlSections.Owner |
942 AccessControlSections.Group |
943 AccessControlSections.Access);
946 public void SetAccessControl (FileSecurity fileSecurity)
948 if (null == fileSecurity)
949 throw new ArgumentNullException ("fileSecurity");
951 fileSecurity.PersistModifications (SafeFileHandle);
956 public override Task FlushAsync (CancellationToken cancellationToken)
958 return base.FlushAsync (cancellationToken);
961 public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
963 return base.ReadAsync (buffer, offset, count, cancellationToken);
966 public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
968 return base.WriteAsync (buffer, offset, count, cancellationToken);
974 // ReadSegment, WriteSegment, FlushBuffer,
975 // RefillBuffer and ReadData should only be called
976 // when the Monitor lock is held, but these methods
977 // grab it again just to be safe.
979 private int ReadSegment (byte [] dest, int dest_offset, int count)
981 count = Math.Min (count, buf_length - buf_offset);
984 // Use the fastest method, all range checks has been done
985 Buffer.BlockCopyInternal (buf, buf_offset, dest, dest_offset, count);
992 private int WriteSegment (byte [] src, int src_offset,
995 if (count > buf_size - buf_offset) {
996 count = buf_size - buf_offset;
1000 Buffer.BlockCopy (src, src_offset,
1003 buf_offset += count;
1004 if (buf_offset > buf_length) {
1005 buf_length = buf_offset;
1014 void FlushBuffer (Stream st)
1019 if (CanSeek == true && safeHandle == null) {
1020 MonoIO.Seek (handle, buf_start,
1023 if (error != MonoIOError.ERROR_SUCCESS) {
1024 // don't leak the path information for isolated storage
1025 throw MonoIO.GetException (GetSecureFileName (name), error);
1029 int wcount = buf_length;
1032 int n = MonoIO.Write (handle, buf, 0, buf_length, out error);
1033 if (error != MonoIOError.ERROR_SUCCESS) {
1034 // don't leak the path information for isolated storage
1035 throw MonoIO.GetException (GetSecureFileName (name), error);
1041 st.Write (buf, 0, buf_length);
1045 buf_start += buf_offset;
1046 buf_offset = buf_length = 0;
1050 private void FlushBuffer ()
1055 private void FlushBufferIfDirty ()
1061 private void RefillBuffer ()
1065 buf_length = ReadData (handle, buf, 0,
1069 private int ReadData (IntPtr handle, byte[] buf, int offset,
1075 /* when async == true, if we get here we don't suport AIO or it's disabled
1076 * and we're using the threadpool */
1077 amount = MonoIO.Read (handle, buf, offset, count, out error);
1078 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1079 amount = 0; // might not be needed, but well...
1080 } else if (error != MonoIOError.ERROR_SUCCESS) {
1081 // don't leak the path information for isolated storage
1082 throw MonoIO.GetException (GetSecureFileName (name), error);
1085 /* Check for read error */
1087 throw new IOException ();
1093 void InitBuffer (int size, bool isZeroSize)
1100 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1102 size = Math.Max (size, 8);
1105 // Instead of allocating a new default buffer use the
1106 // last one if there is any available
1108 if (size <= DefaultBufferSize && buf_recycle != null) {
1109 lock (buf_recycle_lock) {
1110 if (buf_recycle != null) {
1118 buf = new byte [size];
1120 Array.Clear (buf, 0, size);
1125 // buf_offset = buf_length = 0;
1126 // buf_dirty = false;
1129 private string GetSecureFileName (string filename)
1131 return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
1134 private string GetSecureFileName (string filename, bool full)
1136 return (anonymous) ? Path.GetFileName (filename) :
1137 (full) ? Path.GetFullPath (filename) : filename;
1142 internal const int DefaultBufferSize = 4096;
1144 // Input buffer ready for recycling
1145 static byte[] buf_recycle;
1146 static readonly object buf_recycle_lock = new object ();
1148 private byte [] buf; // the buffer
1149 private string name = "[Unknown]"; // name of file.
1151 SafeFileHandle safeHandle; // set only when using one of the
1152 // constructors taking SafeFileHandle
1154 private long append_startpos;
1155 IntPtr handle; // handle to underlying file
1157 private FileAccess access;
1160 private bool canseek;
1161 private bool anonymous;
1162 private bool buf_dirty; // true if buffer has been written to
1164 private int buf_size; // capacity in bytes
1165 private int buf_length; // number of valid bytes in buffer
1166 private int buf_offset; // position of next byte
1167 private long buf_start; // location of buffer in file