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;
103 this.access = access;
104 this.owner = ownsHandle;
105 this.async = isAsync;
107 // default the browser to 'all' anonymous files and let other usage (like smcs) with 'normal'
108 // (i.e. non-anonymous except for isolated storage) files and paths
109 this.anonymous = SecurityManager.SecurityEnabled;
111 this.anonymous = false;
113 InitBuffer (bufferSize, isZeroSize);
116 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
117 if (error != MonoIOError.ERROR_SUCCESS) {
118 throw MonoIO.GetException (name, error);
122 /* Can't set append mode */
123 this.append_startpos=0;
126 // construct from filename
128 public FileStream (string path, FileMode mode)
129 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
133 public FileStream (string path, FileMode mode, FileAccess access)
134 : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
138 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
139 : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
143 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
144 : this (path, mode, access, share, bufferSize, false, FileOptions.None)
148 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
149 : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
153 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
154 : this (path, mode, access, share, bufferSize, false, options)
159 public FileStream (SafeFileHandle handle, FileAccess access)
160 :this(handle, access, DefaultBufferSize, false)
164 public FileStream (SafeFileHandle handle, FileAccess access,
166 :this(handle, access, bufferSize, false)
170 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
171 public FileStream (SafeFileHandle handle, FileAccess access,
172 int bufferSize, bool isAsync)
173 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
175 this.safeHandle = handle;
178 [MonoLimitation ("This ignores the rights parameter")]
179 public FileStream (string path, FileMode mode,
180 FileSystemRights rights, FileShare share,
181 int bufferSize, FileOptions options)
182 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
186 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
187 public FileStream (string path, FileMode mode,
188 FileSystemRights rights, FileShare share,
189 int bufferSize, FileOptions options,
190 FileSecurity fileSecurity)
191 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
196 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
197 : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
201 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
204 throw new ArgumentNullException ("path");
207 if (path.Length == 0) {
208 throw new ArgumentException ("Path is empty");
211 this.anonymous = anonymous;
212 // ignore the Inheritable flag
213 share &= ~FileShare.Inheritable;
216 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
218 if (mode < FileMode.CreateNew || mode > FileMode.Append) {
221 throw new ArgumentException ("mode", "Enum value was out of legal range.");
224 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
227 if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
230 throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range.");
233 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
236 if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
239 throw new IsolatedStorageException ("Enum value for FileShare was out of legal range.");
242 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
245 if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
246 throw new ArgumentException ("Name has invalid chars");
249 if (Directory.Exists (path)) {
250 // don't leak the path information for isolated storage
251 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
252 throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
255 /* Append streams can't be read (see FileMode
258 if (mode==FileMode.Append &&
259 (access&FileAccess.Read)==FileAccess.Read) {
260 throw new ArgumentException("Append access can be requested only in write-only mode.");
263 if ((access & FileAccess.Write) == 0 &&
264 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
265 string msg = Locale.GetText ("Combining FileMode: {0} with " +
266 "FileAccess: {1} is invalid.");
267 throw new ArgumentException (string.Format (msg, access, mode));
270 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
273 if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
274 dname = Path.GetDirectoryName (Path.GetFullPath (path));
276 dname = Path.GetDirectoryName (path);
277 if (dname.Length > 0) {
278 string fp = Path.GetFullPath (dname);
279 if (!Directory.Exists (fp)) {
280 // don't leak the path information for isolated storage
281 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
282 string fname = (anonymous) ? dname : Path.GetFullPath (path);
284 // don't use GetSecureFileName for the directory name
285 throw new IsolatedStorageException (String.Format (msg, fname));
287 throw new DirectoryNotFoundException (String.Format (msg, fname));
292 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
293 mode != FileMode.CreateNew && !File.Exists (path)) {
294 // don't leak the path information for isolated storage
295 string msg = Locale.GetText ("Could not find file \"{0}\".");
296 string fname = GetSecureFileName (path);
298 throw new IsolatedStorageException (String.Format (msg, fname));
300 throw new FileNotFoundException (String.Format (msg, fname), fname);
304 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
308 // TODO: demand permissions
312 this.handle = MonoIO.Open (path, mode, access, share, options, out error);
313 if (handle == MonoIO.InvalidHandle) {
314 // don't leak the path information for isolated storage
315 throw MonoIO.GetException (GetSecureFileName (path), error);
318 this.access = access;
321 /* Can we open non-files by name? */
323 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
325 this.async = (options & FileOptions.Asynchronous) != 0;
327 this.canseek = false;
332 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
333 /* Avoid allocating a large buffer for small files */
335 if (bufferSize > len) {
336 bufferSize = (int)(len < 1000 ? 1000 : len);
340 InitBuffer (bufferSize, false);
342 if (mode==FileMode.Append) {
343 this.Seek (0, SeekOrigin.End);
344 this.append_startpos=this.Position;
346 this.append_startpos=0;
352 public override bool CanRead {
354 return access == FileAccess.Read ||
355 access == FileAccess.ReadWrite;
359 public override bool CanWrite {
361 return access == FileAccess.Write ||
362 access == FileAccess.ReadWrite;
366 public override bool CanSeek {
372 public virtual bool IsAsync {
381 return SecurityManager.CheckElevatedPermissions () ? name : "[Unknown]";
388 public override long Length {
390 if (handle == MonoIO.InvalidHandle)
391 throw new ObjectDisposedException ("Stream has been closed");
394 throw new NotSupportedException ("The stream does not support seeking");
396 // Buffered data might change the length of the stream
397 FlushBufferIfDirty ();
402 length = MonoIO.GetLength (handle, out error);
403 if (error != MonoIOError.ERROR_SUCCESS) {
404 // don't leak the path information for isolated storage
405 throw MonoIO.GetException (GetSecureFileName (name), error);
412 public override long Position {
414 if (handle == MonoIO.InvalidHandle)
415 throw new ObjectDisposedException ("Stream has been closed");
418 throw new NotSupportedException("The stream does not support seeking");
420 return(buf_start + buf_offset);
423 if (handle == MonoIO.InvalidHandle)
424 throw new ObjectDisposedException ("Stream has been closed");
426 if(CanSeek == false) {
427 throw new NotSupportedException("The stream does not support seeking");
431 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
434 Seek (value, SeekOrigin.Begin);
438 [Obsolete ("Use SafeFileHandle instead")]
439 public virtual IntPtr Handle {
440 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
441 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
447 public virtual SafeFileHandle SafeFileHandle {
448 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
449 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
453 if (safeHandle != null)
456 ret = new SafeFileHandle (handle, owner);
465 public override int ReadByte ()
467 if (handle == MonoIO.InvalidHandle)
468 throw new ObjectDisposedException ("Stream has been closed");
471 throw new NotSupportedException ("Stream does not support reading");
474 int n = ReadData (handle, buf, 0, 1);
475 if (n == 0) return -1;
478 else if (buf_offset >= buf_length) {
485 return buf [buf_offset ++];
488 public override void WriteByte (byte value)
490 if (handle == MonoIO.InvalidHandle)
491 throw new ObjectDisposedException ("Stream has been closed");
494 throw new NotSupportedException ("Stream does not support writing");
496 if (buf_offset == buf_size)
499 if (buf_size == 0) { // No buffering
507 buf [buf_offset ++] = value;
508 if (buf_offset > buf_length)
509 buf_length = buf_offset;
514 public override int Read ([In,Out] byte[] array, int offset, int count)
516 if (handle == MonoIO.InvalidHandle)
517 throw new ObjectDisposedException ("Stream has been closed");
519 throw new ArgumentNullException ("array");
521 throw new NotSupportedException ("Stream does not support reading");
522 int len = array.Length;
524 throw new ArgumentOutOfRangeException ("offset", "< 0");
526 throw new ArgumentOutOfRangeException ("count", "< 0");
528 throw new ArgumentException ("destination offset is beyond array size");
529 // reordered to avoid possible integer overflow
530 if (offset > len - count)
531 throw new ArgumentException ("Reading would overrun buffer");
534 IAsyncResult ares = BeginRead (array, offset, count, null, null);
535 return EndRead (ares);
538 return ReadInternal (array, offset, count);
541 int ReadInternal (byte [] dest, int offset, int count)
543 int n = ReadSegment (dest, offset, count);
550 if (count > buf_size) {
551 /* Read as much as we can, up
555 n = ReadData (handle, dest,
559 /* Make the next buffer read
560 * start from the right place
565 n = ReadSegment (dest,
573 delegate int ReadDelegate (byte [] buffer, int offset, int count);
575 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
576 AsyncCallback userCallback, object stateObject)
578 if (handle == MonoIO.InvalidHandle)
579 throw new ObjectDisposedException ("Stream has been closed");
582 throw new NotSupportedException ("This stream does not support reading");
585 throw new ArgumentNullException ("array");
588 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
591 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
593 // reordered to avoid possible integer overflow
594 if (numBytes > array.Length - offset)
595 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
598 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
600 ReadDelegate r = new ReadDelegate (ReadInternal);
601 return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
604 public override int EndRead (IAsyncResult asyncResult)
606 if (asyncResult == null)
607 throw new ArgumentNullException ("asyncResult");
610 return base.EndRead (asyncResult);
612 AsyncResult ares = asyncResult as AsyncResult;
614 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
616 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
618 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
620 return r.EndInvoke (asyncResult);
623 public override void Write (byte[] array, int offset, int count)
625 if (handle == MonoIO.InvalidHandle)
626 throw new ObjectDisposedException ("Stream has been closed");
628 throw new ArgumentNullException ("array");
630 throw new ArgumentOutOfRangeException ("offset", "< 0");
632 throw new ArgumentOutOfRangeException ("count", "< 0");
633 // ordered to avoid possible integer overflow
634 if (offset > array.Length - count)
635 throw new ArgumentException ("Reading would overrun buffer");
637 throw new NotSupportedException ("Stream does not support writing");
640 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
645 WriteInternal (array, offset, count);
648 void WriteInternal (byte [] src, int offset, int count)
650 if (count > buf_size) {
651 // shortcut for long writes
658 int n = MonoIO.Write (handle, src, offset, wcount, out error);
659 if (error != MonoIOError.ERROR_SUCCESS)
660 throw MonoIO.GetException (GetSecureFileName (name), error);
671 int n = WriteSegment (src, offset + copied, count);
684 delegate void WriteDelegate (byte [] buffer, int offset, int count);
686 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
687 AsyncCallback userCallback, object stateObject)
689 if (handle == MonoIO.InvalidHandle)
690 throw new ObjectDisposedException ("Stream has been closed");
693 throw new NotSupportedException ("This stream does not support writing");
696 throw new ArgumentNullException ("array");
699 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
702 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
704 // reordered to avoid possible integer overflow
705 if (numBytes > array.Length - offset)
706 throw new ArgumentException ("array too small. numBytes/offset wrong.");
709 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
711 FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
712 result.BytesRead = -1;
713 result.Count = numBytes;
714 result.OriginalCount = numBytes;
717 MemoryStream ms = new MemoryStream ();
719 ms.Write (array, offset, numBytes);
721 numBytes = (int) ms.Length;
724 WriteDelegate w = new WriteDelegate (WriteInternal);
725 return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
728 public override void EndWrite (IAsyncResult asyncResult)
730 if (asyncResult == null)
731 throw new ArgumentNullException ("asyncResult");
734 base.EndWrite (asyncResult);
738 AsyncResult ares = asyncResult as AsyncResult;
740 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
742 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
744 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
746 w.EndInvoke (asyncResult);
750 public override long Seek (long offset, SeekOrigin origin)
754 if (handle == MonoIO.InvalidHandle)
755 throw new ObjectDisposedException ("Stream has been closed");
759 if(CanSeek == false) {
760 throw new NotSupportedException("The stream does not support seeking");
765 pos = Length + offset;
768 case SeekOrigin.Current:
769 pos = Position + offset;
772 case SeekOrigin.Begin:
777 throw new ArgumentException ("origin", "Invalid SeekOrigin");
781 /* LAMESPEC: shouldn't this be
782 * ArgumentOutOfRangeException?
784 throw new IOException("Attempted to Seek before the beginning of the stream");
787 if(pos < this.append_startpos) {
788 /* More undocumented crap */
789 throw new IOException("Can't seek back over pre-existing data in append mode");
796 buf_start = MonoIO.Seek (handle, pos,
800 if (error != MonoIOError.ERROR_SUCCESS) {
801 // don't leak the path information for isolated storage
802 throw MonoIO.GetException (GetSecureFileName (name), error);
808 public override void SetLength (long value)
810 if (handle == MonoIO.InvalidHandle)
811 throw new ObjectDisposedException ("Stream has been closed");
814 throw new NotSupportedException("The stream does not support seeking");
816 if(CanWrite == false)
817 throw new NotSupportedException("The stream does not support writing");
820 throw new ArgumentOutOfRangeException("value is less than 0");
826 MonoIO.SetLength (handle, value, out error);
827 if (error != MonoIOError.ERROR_SUCCESS) {
828 // don't leak the path information for isolated storage
829 throw MonoIO.GetException (GetSecureFileName (name), error);
832 if (Position > value)
836 public override void Flush ()
838 if (handle == MonoIO.InvalidHandle)
839 throw new ObjectDisposedException ("Stream has been closed");
844 #if NET_4_0 || MOONLIGHT || MOBILE
845 public virtual void Flush (bool flushToDisk)
849 // This does the fsync
852 MonoIO.Flush (handle, out error);
857 public virtual void Lock (long position, long length)
859 if (handle == MonoIO.InvalidHandle)
860 throw new ObjectDisposedException ("Stream has been closed");
862 throw new ArgumentOutOfRangeException ("position must not be negative");
865 throw new ArgumentOutOfRangeException ("length must not be negative");
867 if (handle == MonoIO.InvalidHandle) {
868 throw new ObjectDisposedException ("Stream has been closed");
873 MonoIO.Lock (handle, position, length, out error);
874 if (error != MonoIOError.ERROR_SUCCESS) {
875 // don't leak the path information for isolated storage
876 throw MonoIO.GetException (GetSecureFileName (name), error);
880 public virtual void Unlock (long position, long length)
882 if (handle == MonoIO.InvalidHandle)
883 throw new ObjectDisposedException ("Stream has been closed");
885 throw new ArgumentOutOfRangeException ("position must not be negative");
888 throw new ArgumentOutOfRangeException ("length must not be negative");
893 MonoIO.Unlock (handle, position, length, out error);
894 if (error != MonoIOError.ERROR_SUCCESS) {
895 // don't leak the path information for isolated storage
896 throw MonoIO.GetException (GetSecureFileName (name), error);
907 protected override void Dispose (bool disposing)
909 Exception exc = null;
910 if (handle != MonoIO.InvalidHandle) {
913 } catch (Exception e) {
920 MonoIO.Close (handle, out error);
921 if (error != MonoIOError.ERROR_SUCCESS) {
922 // don't leak the path information for isolated storage
923 throw MonoIO.GetException (GetSecureFileName (name), error);
926 handle = MonoIO.InvalidHandle;
933 if (disposing && buf != null) {
934 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
935 lock (buf_recycle_lock) {
936 if (buf_recycle == null) {
943 GC.SuppressFinalize (this);
950 public FileSecurity GetAccessControl ()
952 throw new NotImplementedException ();
955 public void SetAccessControl (FileSecurity fileSecurity)
957 throw new NotImplementedException ();
962 public override Task FlushAsync (CancellationToken cancellationToken)
964 return base.FlushAsync (cancellationToken);
967 public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
969 return base.ReadAsync (buffer, offset, count, cancellationToken);
972 public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
974 return base.WriteAsync (buffer, offset, count, cancellationToken);
980 // ReadSegment, WriteSegment, FlushBuffer,
981 // RefillBuffer and ReadData should only be called
982 // when the Monitor lock is held, but these methods
983 // grab it again just to be safe.
985 private int ReadSegment (byte [] dest, int dest_offset, int count)
987 count = Math.Min (count, buf_length - buf_offset);
990 // Use the fastest method, all range checks has been done
991 Buffer.BlockCopyInternal (buf, buf_offset, dest, dest_offset, count);
998 private int WriteSegment (byte [] src, int src_offset,
1001 if (count > buf_size - buf_offset) {
1002 count = buf_size - buf_offset;
1006 Buffer.BlockCopy (src, src_offset,
1009 buf_offset += count;
1010 if (buf_offset > buf_length) {
1011 buf_length = buf_offset;
1020 void FlushBuffer (Stream st)
1025 if (CanSeek == true) {
1026 MonoIO.Seek (handle, buf_start,
1029 if (error != MonoIOError.ERROR_SUCCESS) {
1030 // don't leak the path information for isolated storage
1031 throw MonoIO.GetException (GetSecureFileName (name), error);
1035 int wcount = buf_length;
1038 int n = MonoIO.Write (handle, buf, 0, buf_length, out error);
1039 if (error != MonoIOError.ERROR_SUCCESS) {
1040 // don't leak the path information for isolated storage
1041 throw MonoIO.GetException (GetSecureFileName (name), error);
1047 st.Write (buf, 0, buf_length);
1051 buf_start += buf_offset;
1052 buf_offset = buf_length = 0;
1056 private void FlushBuffer ()
1061 private void FlushBufferIfDirty ()
1067 private void RefillBuffer ()
1071 buf_length = ReadData (handle, buf, 0,
1075 private int ReadData (IntPtr handle, byte[] buf, int offset,
1081 /* when async == true, if we get here we don't suport AIO or it's disabled
1082 * and we're using the threadpool */
1083 amount = MonoIO.Read (handle, buf, offset, count, out error);
1084 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1085 amount = 0; // might not be needed, but well...
1086 } else if (error != MonoIOError.ERROR_SUCCESS) {
1087 // don't leak the path information for isolated storage
1088 throw MonoIO.GetException (GetSecureFileName (name), error);
1091 /* Check for read error */
1093 throw new IOException ();
1099 void InitBuffer (int size, bool isZeroSize)
1106 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1108 size = Math.Max (size, 8);
1111 // Instead of allocating a new default buffer use the
1112 // last one if there is any available
1114 if (size <= DefaultBufferSize && buf_recycle != null) {
1115 lock (buf_recycle_lock) {
1116 if (buf_recycle != null) {
1124 buf = new byte [size];
1126 Array.Clear (buf, 0, size);
1131 // buf_offset = buf_length = 0;
1132 // buf_dirty = false;
1135 private string GetSecureFileName (string filename)
1137 return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
1140 private string GetSecureFileName (string filename, bool full)
1142 return (anonymous) ? Path.GetFileName (filename) :
1143 (full) ? Path.GetFullPath (filename) : filename;
1148 internal const int DefaultBufferSize = 8192;
1150 // Input buffer ready for recycling
1151 static byte[] buf_recycle;
1152 static readonly object buf_recycle_lock = new object ();
1154 private byte [] buf; // the buffer
1155 private string name = "[Unknown]"; // name of file.
1157 SafeFileHandle safeHandle; // set only when using one of the
1158 // constructors taking SafeFileHandle
1160 private long append_startpos;
1161 IntPtr handle; // handle to underlying file
1163 private FileAccess access;
1166 private bool canseek;
1167 private bool anonymous;
1168 private bool buf_dirty; // true if buffer has been written to
1170 private int buf_size; // capacity in bytes
1171 private int buf_length; // number of valid bytes in buffer
1172 private int buf_offset; // position of next byte
1173 private long buf_start; // location of buffer in file