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;
108 InitBuffer (bufferSize, isZeroSize);
111 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
112 if (error != MonoIOError.ERROR_SUCCESS) {
113 throw MonoIO.GetException (name, error);
117 /* Can't set append mode */
118 this.append_startpos=0;
121 // construct from filename
123 public FileStream (string path, FileMode mode)
124 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
128 public FileStream (string path, FileMode mode, FileAccess access)
129 : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
133 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
134 : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
138 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
139 : this (path, mode, access, share, bufferSize, false, FileOptions.None)
143 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
144 : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
148 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
149 : this (path, mode, access, share, bufferSize, false, options)
154 public FileStream (SafeFileHandle handle, FileAccess access)
155 :this(handle, access, DefaultBufferSize, false)
159 public FileStream (SafeFileHandle handle, FileAccess access,
161 :this(handle, access, bufferSize, false)
165 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
166 public FileStream (SafeFileHandle handle, FileAccess access,
167 int bufferSize, bool isAsync)
168 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
170 this.safeHandle = handle;
173 [MonoLimitation ("This ignores the rights parameter")]
174 public FileStream (string path, FileMode mode,
175 FileSystemRights rights, FileShare share,
176 int bufferSize, FileOptions options)
177 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
181 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
182 public FileStream (string path, FileMode mode,
183 FileSystemRights rights, FileShare share,
184 int bufferSize, FileOptions options,
185 FileSecurity fileSecurity)
186 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
191 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
192 : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
196 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
199 throw new ArgumentNullException ("path");
202 if (path.Length == 0) {
203 throw new ArgumentException ("Path is empty");
206 this.anonymous = anonymous;
207 // ignore the Inheritable flag
208 share &= ~FileShare.Inheritable;
211 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
213 if (mode < FileMode.CreateNew || mode > FileMode.Append) {
216 throw new ArgumentException ("mode", "Enum value was out of legal range.");
219 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
222 if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
225 throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range.");
228 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
231 if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
234 throw new IsolatedStorageException ("Enum value for FileShare was out of legal range.");
237 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
240 if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
241 throw new ArgumentException ("Name has invalid chars");
244 if (Directory.Exists (path)) {
245 // don't leak the path information for isolated storage
246 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
247 throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
250 /* Append streams can't be read (see FileMode
253 if (mode==FileMode.Append &&
254 (access&FileAccess.Read)==FileAccess.Read) {
255 throw new ArgumentException("Append access can be requested only in write-only mode.");
258 if ((access & FileAccess.Write) == 0 &&
259 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
260 string msg = Locale.GetText ("Combining FileMode: {0} with " +
261 "FileAccess: {1} is invalid.");
262 throw new ArgumentException (string.Format (msg, access, mode));
265 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
268 if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
269 dname = Path.GetDirectoryName (Path.GetFullPath (path));
271 dname = Path.GetDirectoryName (path);
272 if (dname.Length > 0) {
273 string fp = Path.GetFullPath (dname);
274 if (!Directory.Exists (fp)) {
275 // don't leak the path information for isolated storage
276 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
277 string fname = (anonymous) ? dname : Path.GetFullPath (path);
279 // don't use GetSecureFileName for the directory name
280 throw new IsolatedStorageException (String.Format (msg, fname));
282 throw new DirectoryNotFoundException (String.Format (msg, fname));
287 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
288 mode != FileMode.CreateNew && !File.Exists (path)) {
289 // don't leak the path information for isolated storage
290 string msg = Locale.GetText ("Could not find file \"{0}\".");
291 string fname = GetSecureFileName (path);
293 throw new IsolatedStorageException (String.Format (msg, fname));
295 throw new FileNotFoundException (String.Format (msg, fname), fname);
299 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
303 // TODO: demand permissions
307 this.handle = MonoIO.Open (path, mode, access, share, options, out error);
308 if (handle == MonoIO.InvalidHandle) {
309 // don't leak the path information for isolated storage
310 throw MonoIO.GetException (GetSecureFileName (path), error);
313 this.access = access;
316 /* Can we open non-files by name? */
318 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
320 this.async = (options & FileOptions.Asynchronous) != 0;
322 this.canseek = false;
327 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
328 /* Avoid allocating a large buffer for small files */
330 if (bufferSize > len) {
331 bufferSize = (int)(len < 1000 ? 1000 : len);
335 InitBuffer (bufferSize, false);
337 if (mode==FileMode.Append) {
338 this.Seek (0, SeekOrigin.End);
339 this.append_startpos=this.Position;
341 this.append_startpos=0;
347 public override bool CanRead {
349 return access == FileAccess.Read ||
350 access == FileAccess.ReadWrite;
354 public override bool CanWrite {
356 return access == FileAccess.Write ||
357 access == FileAccess.ReadWrite;
361 public override bool CanSeek {
367 public virtual bool IsAsync {
376 return SecurityManager.CheckElevatedPermissions () ? name : "[Unknown]";
383 public override long Length {
385 if (handle == MonoIO.InvalidHandle)
386 throw new ObjectDisposedException ("Stream has been closed");
389 throw new NotSupportedException ("The stream does not support seeking");
391 // Buffered data might change the length of the stream
392 FlushBufferIfDirty ();
397 length = MonoIO.GetLength (handle, out error);
398 if (error != MonoIOError.ERROR_SUCCESS) {
399 // don't leak the path information for isolated storage
400 throw MonoIO.GetException (GetSecureFileName (name), error);
407 public override long Position {
409 if (handle == MonoIO.InvalidHandle)
410 throw new ObjectDisposedException ("Stream has been closed");
413 throw new NotSupportedException("The stream does not support seeking");
415 return(buf_start + buf_offset);
418 if (handle == MonoIO.InvalidHandle)
419 throw new ObjectDisposedException ("Stream has been closed");
421 if(CanSeek == false) {
422 throw new NotSupportedException("The stream does not support seeking");
426 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
429 Seek (value, SeekOrigin.Begin);
433 [Obsolete ("Use SafeFileHandle instead")]
434 public virtual IntPtr Handle {
435 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
436 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
442 public virtual SafeFileHandle SafeFileHandle {
443 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
444 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
448 if (safeHandle != null)
451 ret = new SafeFileHandle (handle, owner);
460 public override int ReadByte ()
462 if (handle == MonoIO.InvalidHandle)
463 throw new ObjectDisposedException ("Stream has been closed");
466 throw new NotSupportedException ("Stream does not support reading");
469 int n = ReadData (handle, buf, 0, 1);
470 if (n == 0) return -1;
473 else if (buf_offset >= buf_length) {
480 return buf [buf_offset ++];
483 public override void WriteByte (byte value)
485 if (handle == MonoIO.InvalidHandle)
486 throw new ObjectDisposedException ("Stream has been closed");
489 throw new NotSupportedException ("Stream does not support writing");
491 if (buf_offset == buf_size)
494 if (buf_size == 0) { // No buffering
502 buf [buf_offset ++] = value;
503 if (buf_offset > buf_length)
504 buf_length = buf_offset;
509 public override int Read ([In,Out] byte[] array, int offset, int count)
511 if (handle == MonoIO.InvalidHandle)
512 throw new ObjectDisposedException ("Stream has been closed");
514 throw new ArgumentNullException ("array");
516 throw new NotSupportedException ("Stream does not support reading");
517 int len = array.Length;
519 throw new ArgumentOutOfRangeException ("offset", "< 0");
521 throw new ArgumentOutOfRangeException ("count", "< 0");
523 throw new ArgumentException ("destination offset is beyond array size");
524 // reordered to avoid possible integer overflow
525 if (offset > len - count)
526 throw new ArgumentException ("Reading would overrun buffer");
529 IAsyncResult ares = BeginRead (array, offset, count, null, null);
530 return EndRead (ares);
533 return ReadInternal (array, offset, count);
536 int ReadInternal (byte [] dest, int offset, int count)
538 int n = ReadSegment (dest, offset, count);
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,
568 delegate int ReadDelegate (byte [] buffer, int offset, int count);
570 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
571 AsyncCallback userCallback, object stateObject)
573 if (handle == MonoIO.InvalidHandle)
574 throw new ObjectDisposedException ("Stream has been closed");
577 throw new NotSupportedException ("This stream does not support reading");
580 throw new ArgumentNullException ("array");
583 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
586 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
588 // reordered to avoid possible integer overflow
589 if (numBytes > array.Length - offset)
590 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
593 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
595 ReadDelegate r = new ReadDelegate (ReadInternal);
596 return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
599 public override int EndRead (IAsyncResult asyncResult)
601 if (asyncResult == null)
602 throw new ArgumentNullException ("asyncResult");
605 return base.EndRead (asyncResult);
607 AsyncResult ares = asyncResult as AsyncResult;
609 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
611 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
613 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
615 return r.EndInvoke (asyncResult);
618 public override void Write (byte[] array, int offset, int count)
620 if (handle == MonoIO.InvalidHandle)
621 throw new ObjectDisposedException ("Stream has been closed");
623 throw new ArgumentNullException ("array");
625 throw new ArgumentOutOfRangeException ("offset", "< 0");
627 throw new ArgumentOutOfRangeException ("count", "< 0");
628 // ordered to avoid possible integer overflow
629 if (offset > array.Length - count)
630 throw new ArgumentException ("Reading would overrun buffer");
632 throw new NotSupportedException ("Stream does not support writing");
635 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
640 WriteInternal (array, offset, count);
643 void WriteInternal (byte [] src, int offset, int count)
645 if (count > buf_size) {
646 // shortcut for long writes
653 int n = MonoIO.Write (handle, src, offset, wcount, out error);
654 if (error != MonoIOError.ERROR_SUCCESS)
655 throw MonoIO.GetException (GetSecureFileName (name), error);
666 int n = WriteSegment (src, offset + copied, count);
679 delegate void WriteDelegate (byte [] buffer, int offset, int count);
681 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
682 AsyncCallback userCallback, object stateObject)
684 if (handle == MonoIO.InvalidHandle)
685 throw new ObjectDisposedException ("Stream has been closed");
688 throw new NotSupportedException ("This stream does not support writing");
691 throw new ArgumentNullException ("array");
694 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
697 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
699 // reordered to avoid possible integer overflow
700 if (numBytes > array.Length - offset)
701 throw new ArgumentException ("array too small. numBytes/offset wrong.");
704 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
706 FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
707 result.BytesRead = -1;
708 result.Count = numBytes;
709 result.OriginalCount = numBytes;
712 MemoryStream ms = new MemoryStream ();
714 ms.Write (array, offset, numBytes);
716 numBytes = (int) ms.Length;
719 WriteDelegate w = new WriteDelegate (WriteInternal);
720 return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
723 public override void EndWrite (IAsyncResult asyncResult)
725 if (asyncResult == null)
726 throw new ArgumentNullException ("asyncResult");
729 base.EndWrite (asyncResult);
733 AsyncResult ares = asyncResult as AsyncResult;
735 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
737 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
739 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
741 w.EndInvoke (asyncResult);
745 public override long Seek (long offset, SeekOrigin origin)
749 if (handle == MonoIO.InvalidHandle)
750 throw new ObjectDisposedException ("Stream has been closed");
754 if(CanSeek == false) {
755 throw new NotSupportedException("The stream does not support seeking");
760 pos = Length + offset;
763 case SeekOrigin.Current:
764 pos = Position + offset;
767 case SeekOrigin.Begin:
772 throw new ArgumentException ("origin", "Invalid SeekOrigin");
776 /* LAMESPEC: shouldn't this be
777 * ArgumentOutOfRangeException?
779 throw new IOException("Attempted to Seek before the beginning of the stream");
782 if(pos < this.append_startpos) {
783 /* More undocumented crap */
784 throw new IOException("Can't seek back over pre-existing data in append mode");
791 buf_start = MonoIO.Seek (handle, pos,
795 if (error != MonoIOError.ERROR_SUCCESS) {
796 // don't leak the path information for isolated storage
797 throw MonoIO.GetException (GetSecureFileName (name), error);
803 public override void SetLength (long value)
805 if (handle == MonoIO.InvalidHandle)
806 throw new ObjectDisposedException ("Stream has been closed");
809 throw new NotSupportedException("The stream does not support seeking");
811 if(CanWrite == false)
812 throw new NotSupportedException("The stream does not support writing");
815 throw new ArgumentOutOfRangeException("value is less than 0");
821 MonoIO.SetLength (handle, value, out error);
822 if (error != MonoIOError.ERROR_SUCCESS) {
823 // don't leak the path information for isolated storage
824 throw MonoIO.GetException (GetSecureFileName (name), error);
827 if (Position > value)
831 public override void Flush ()
833 if (handle == MonoIO.InvalidHandle)
834 throw new ObjectDisposedException ("Stream has been closed");
839 #if NET_4_0 || MOONLIGHT || MOBILE
840 public virtual void Flush (bool flushToDisk)
844 // This does the fsync
847 MonoIO.Flush (handle, out error);
852 public virtual void Lock (long position, long length)
854 if (handle == MonoIO.InvalidHandle)
855 throw new ObjectDisposedException ("Stream has been closed");
857 throw new ArgumentOutOfRangeException ("position must not be negative");
860 throw new ArgumentOutOfRangeException ("length must not be negative");
862 if (handle == MonoIO.InvalidHandle) {
863 throw new ObjectDisposedException ("Stream has been closed");
868 MonoIO.Lock (handle, position, length, out error);
869 if (error != MonoIOError.ERROR_SUCCESS) {
870 // don't leak the path information for isolated storage
871 throw MonoIO.GetException (GetSecureFileName (name), error);
875 public virtual void Unlock (long position, long length)
877 if (handle == MonoIO.InvalidHandle)
878 throw new ObjectDisposedException ("Stream has been closed");
880 throw new ArgumentOutOfRangeException ("position must not be negative");
883 throw new ArgumentOutOfRangeException ("length must not be negative");
888 MonoIO.Unlock (handle, position, length, out error);
889 if (error != MonoIOError.ERROR_SUCCESS) {
890 // don't leak the path information for isolated storage
891 throw MonoIO.GetException (GetSecureFileName (name), error);
902 protected override void Dispose (bool disposing)
904 Exception exc = null;
905 if (handle != MonoIO.InvalidHandle) {
908 } catch (Exception e) {
915 MonoIO.Close (handle, out error);
916 if (error != MonoIOError.ERROR_SUCCESS) {
917 // don't leak the path information for isolated storage
918 throw MonoIO.GetException (GetSecureFileName (name), error);
921 handle = MonoIO.InvalidHandle;
928 if (disposing && buf != null) {
929 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
930 lock (buf_recycle_lock) {
931 if (buf_recycle == null) {
938 GC.SuppressFinalize (this);
945 public FileSecurity GetAccessControl ()
947 throw new NotImplementedException ();
950 public void SetAccessControl (FileSecurity fileSecurity)
952 throw new NotImplementedException ();
958 // ReadSegment, WriteSegment, FlushBuffer,
959 // RefillBuffer and ReadData should only be called
960 // when the Monitor lock is held, but these methods
961 // grab it again just to be safe.
963 private int ReadSegment (byte [] dest, int dest_offset, int count)
965 count = Math.Min (count, buf_length - buf_offset);
968 // Use the fastest method, all range checks has been done
969 Buffer.BlockCopyInternal (buf, buf_offset, dest, dest_offset, count);
976 private int WriteSegment (byte [] src, int src_offset,
979 if (count > buf_size - buf_offset) {
980 count = buf_size - buf_offset;
984 Buffer.BlockCopy (src, src_offset,
988 if (buf_offset > buf_length) {
989 buf_length = buf_offset;
998 void FlushBuffer (Stream st)
1003 if (CanSeek == true) {
1004 MonoIO.Seek (handle, buf_start,
1007 if (error != MonoIOError.ERROR_SUCCESS) {
1008 // don't leak the path information for isolated storage
1009 throw MonoIO.GetException (GetSecureFileName (name), error);
1013 int wcount = buf_length;
1016 int n = MonoIO.Write (handle, buf, 0, buf_length, out error);
1017 if (error != MonoIOError.ERROR_SUCCESS) {
1018 // don't leak the path information for isolated storage
1019 throw MonoIO.GetException (GetSecureFileName (name), error);
1025 st.Write (buf, 0, buf_length);
1029 buf_start += buf_offset;
1030 buf_offset = buf_length = 0;
1034 private void FlushBuffer ()
1039 private void FlushBufferIfDirty ()
1045 private void RefillBuffer ()
1049 buf_length = ReadData (handle, buf, 0,
1053 private int ReadData (IntPtr handle, byte[] buf, int offset,
1059 /* when async == true, if we get here we don't suport AIO or it's disabled
1060 * and we're using the threadpool */
1061 amount = MonoIO.Read (handle, buf, offset, count, out error);
1062 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1063 amount = 0; // might not be needed, but well...
1064 } else if (error != MonoIOError.ERROR_SUCCESS) {
1065 // don't leak the path information for isolated storage
1066 throw MonoIO.GetException (GetSecureFileName (name), error);
1069 /* Check for read error */
1071 throw new IOException ();
1077 void InitBuffer (int size, bool isZeroSize)
1084 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1086 size = Math.Max (size, 8);
1089 // Instead of allocating a new default buffer use the
1090 // last one if there is any available
1092 if (size <= DefaultBufferSize && buf_recycle != null) {
1093 lock (buf_recycle_lock) {
1094 if (buf_recycle != null) {
1102 buf = new byte [size];
1104 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