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;
108 // default the browser to 'all' anonymous files and let other usage (like smcs) with 'normal'
109 // (i.e. non-anonymous except for isolated storage) files and paths
110 this.anonymous = SecurityManager.SecurityEnabled;
112 this.anonymous = false;
115 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
116 if (error != MonoIOError.ERROR_SUCCESS) {
117 throw MonoIO.GetException (name, error);
121 /* Can't set append mode */
122 this.append_startpos=0;
125 // construct from filename
127 public FileStream (string path, FileMode mode)
128 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
132 public FileStream (string path, FileMode mode, FileAccess access)
133 : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
137 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
138 : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
142 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
143 : this (path, mode, access, share, bufferSize, false, FileOptions.None)
147 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
148 : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : 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)
158 public FileStream (SafeFileHandle handle, FileAccess access)
159 :this(handle, access, DefaultBufferSize, false)
163 public FileStream (SafeFileHandle handle, FileAccess access,
165 :this(handle, access, bufferSize, false)
169 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
170 public FileStream (SafeFileHandle handle, FileAccess access,
171 int bufferSize, bool isAsync)
172 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
174 this.safeHandle = handle;
177 [MonoLimitation ("This ignores the rights parameter")]
178 public FileStream (string path, FileMode mode,
179 FileSystemRights rights, FileShare share,
180 int bufferSize, FileOptions options)
181 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
185 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
186 public FileStream (string path, FileMode mode,
187 FileSystemRights rights, FileShare share,
188 int bufferSize, FileOptions options,
189 FileSecurity fileSecurity)
190 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
195 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
196 : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
200 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
203 throw new ArgumentNullException ("path");
206 if (path.Length == 0) {
207 throw new ArgumentException ("Path is empty");
210 this.anonymous = anonymous;
211 // ignore the Inheritable flag
212 share &= ~FileShare.Inheritable;
215 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
217 if (mode < FileMode.CreateNew || mode > FileMode.Append) {
220 throw new ArgumentException ("mode", "Enum value was out of legal range.");
223 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
226 if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
229 throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range.");
232 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
235 if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
238 throw new IsolatedStorageException ("Enum value for FileShare was out of legal range.");
241 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
244 if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
245 throw new ArgumentException ("Name has invalid chars");
248 if (Directory.Exists (path)) {
249 // don't leak the path information for isolated storage
250 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
251 throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
254 /* Append streams can't be read (see FileMode
257 if (mode==FileMode.Append &&
258 (access&FileAccess.Read)==FileAccess.Read) {
259 throw new ArgumentException("Append access can be requested only in write-only mode.");
262 if ((access & FileAccess.Write) == 0 &&
263 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
264 string msg = Locale.GetText ("Combining FileMode: {0} with " +
265 "FileAccess: {1} is invalid.");
266 throw new ArgumentException (string.Format (msg, access, mode));
269 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
272 if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
273 dname = Path.GetDirectoryName (Path.GetFullPath (path));
275 dname = Path.GetDirectoryName (path);
276 if (dname.Length > 0) {
277 string fp = Path.GetFullPath (dname);
278 if (!Directory.Exists (fp)) {
279 // don't leak the path information for isolated storage
280 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
281 string fname = (anonymous) ? dname : Path.GetFullPath (path);
283 // don't use GetSecureFileName for the directory name
284 throw new IsolatedStorageException (String.Format (msg, fname));
286 throw new DirectoryNotFoundException (String.Format (msg, fname));
291 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
292 mode != FileMode.CreateNew && !File.Exists (path)) {
293 // don't leak the path information for isolated storage
294 string msg = Locale.GetText ("Could not find file \"{0}\".");
295 string fname = GetSecureFileName (path);
297 throw new IsolatedStorageException (String.Format (msg, fname));
299 throw new FileNotFoundException (String.Format (msg, fname), fname);
303 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
307 // TODO: demand permissions
311 this.handle = MonoIO.Open (path, mode, access, share, options, out error);
312 if (handle == MonoIO.InvalidHandle) {
313 // don't leak the path information for isolated storage
314 throw MonoIO.GetException (GetSecureFileName (path), error);
317 this.access = access;
320 /* Can we open non-files by name? */
322 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
324 this.async = (options & FileOptions.Asynchronous) != 0;
326 this.canseek = false;
331 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
332 /* Avoid allocating a large buffer for small files */
334 if (bufferSize > len) {
335 bufferSize = (int)(len < 1000 ? 1000 : len);
339 InitBuffer (bufferSize, false);
341 if (mode==FileMode.Append) {
342 this.Seek (0, SeekOrigin.End);
343 this.append_startpos=this.Position;
345 this.append_startpos=0;
351 public override bool CanRead {
353 return access == FileAccess.Read ||
354 access == FileAccess.ReadWrite;
358 public override bool CanWrite {
360 return access == FileAccess.Write ||
361 access == FileAccess.ReadWrite;
365 public override bool CanSeek {
371 public virtual bool IsAsync {
380 return SecurityManager.CheckElevatedPermissions () ? name : "[Unknown]";
387 public override long Length {
389 if (handle == MonoIO.InvalidHandle)
390 throw new ObjectDisposedException ("Stream has been closed");
393 throw new NotSupportedException ("The stream does not support seeking");
395 // Buffered data might change the length of the stream
396 FlushBufferIfDirty ();
401 length = MonoIO.GetLength (handle, out error);
402 if (error != MonoIOError.ERROR_SUCCESS) {
403 // don't leak the path information for isolated storage
404 throw MonoIO.GetException (GetSecureFileName (name), error);
411 public override long Position {
413 if (handle == MonoIO.InvalidHandle)
414 throw new ObjectDisposedException ("Stream has been closed");
416 if (CanSeek == false)
417 throw new NotSupportedException("The stream does not support seeking");
419 if (safeHandle != null) {
420 // If the handle was leaked outside we always ask the real handle
423 long ret = MonoIO.Seek (handle, 0,SeekOrigin.Current,out error);
425 if (error != MonoIOError.ERROR_SUCCESS) {
426 // don't leak the path information for isolated storage
427 throw MonoIO.GetException (GetSecureFileName (name), error);
433 return(buf_start + buf_offset);
436 if (handle == MonoIO.InvalidHandle)
437 throw new ObjectDisposedException ("Stream has been closed");
439 if(CanSeek == false) {
440 throw new NotSupportedException("The stream does not support seeking");
444 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
447 Seek (value, SeekOrigin.Begin);
451 [Obsolete ("Use SafeFileHandle instead")]
452 public virtual IntPtr Handle {
453 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
454 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
456 if (safeHandle == null) {
463 public virtual SafeFileHandle SafeFileHandle {
464 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
465 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
467 if (safeHandle == null) {
478 safeHandle = new SafeFileHandle (handle, false);
480 InitBuffer (0, true);
483 public override int ReadByte ()
485 if (handle == MonoIO.InvalidHandle)
486 throw new ObjectDisposedException ("Stream has been closed");
489 throw new NotSupportedException ("Stream does not support reading");
492 int n = ReadData (handle, buf, 0, 1);
493 if (n == 0) return -1;
496 else if (buf_offset >= buf_length) {
503 return buf [buf_offset ++];
506 public override void WriteByte (byte value)
508 if (handle == MonoIO.InvalidHandle)
509 throw new ObjectDisposedException ("Stream has been closed");
512 throw new NotSupportedException ("Stream does not support writing");
514 if (buf_offset == buf_size)
517 if (buf_size == 0) { // No buffering
525 buf [buf_offset ++] = value;
526 if (buf_offset > buf_length)
527 buf_length = buf_offset;
532 public override int Read ([In,Out] byte[] array, int offset, int count)
534 if (handle == MonoIO.InvalidHandle)
535 throw new ObjectDisposedException ("Stream has been closed");
537 throw new ArgumentNullException ("array");
539 throw new NotSupportedException ("Stream does not support reading");
540 int len = array.Length;
542 throw new ArgumentOutOfRangeException ("offset", "< 0");
544 throw new ArgumentOutOfRangeException ("count", "< 0");
546 throw new ArgumentException ("destination offset is beyond array size");
547 // reordered to avoid possible integer overflow
548 if (offset > len - count)
549 throw new ArgumentException ("Reading would overrun buffer");
552 IAsyncResult ares = BeginRead (array, offset, count, null, null);
553 return EndRead (ares);
556 return ReadInternal (array, offset, count);
559 int ReadInternal (byte [] dest, int offset, int count)
561 int n = ReadSegment (dest, offset, count);
568 if (count > buf_size) {
569 /* Read as much as we can, up
573 n = ReadData (handle, dest,
577 /* Make the next buffer read
578 * start from the right place
583 n = ReadSegment (dest,
591 delegate int ReadDelegate (byte [] buffer, int offset, int count);
593 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
594 AsyncCallback userCallback, object stateObject)
596 if (handle == MonoIO.InvalidHandle)
597 throw new ObjectDisposedException ("Stream has been closed");
600 throw new NotSupportedException ("This stream does not support reading");
603 throw new ArgumentNullException ("array");
606 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
609 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
611 // reordered to avoid possible integer overflow
612 if (numBytes > array.Length - offset)
613 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
616 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
618 ReadDelegate r = new ReadDelegate (ReadInternal);
619 return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
622 public override int EndRead (IAsyncResult asyncResult)
624 if (asyncResult == null)
625 throw new ArgumentNullException ("asyncResult");
628 return base.EndRead (asyncResult);
630 AsyncResult ares = asyncResult as AsyncResult;
632 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
634 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
636 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
638 return r.EndInvoke (asyncResult);
641 public override void Write (byte[] array, int offset, int count)
643 if (handle == MonoIO.InvalidHandle)
644 throw new ObjectDisposedException ("Stream has been closed");
646 throw new ArgumentNullException ("array");
648 throw new ArgumentOutOfRangeException ("offset", "< 0");
650 throw new ArgumentOutOfRangeException ("count", "< 0");
651 // ordered to avoid possible integer overflow
652 if (offset > array.Length - count)
653 throw new ArgumentException ("Reading would overrun buffer");
655 throw new NotSupportedException ("Stream does not support writing");
658 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
663 WriteInternal (array, offset, count);
666 void WriteInternal (byte [] src, int offset, int count)
668 if (count > buf_size) {
669 // shortcut for long writes
676 int n = MonoIO.Write (handle, src, offset, wcount, out error);
677 if (error != MonoIOError.ERROR_SUCCESS)
678 throw MonoIO.GetException (GetSecureFileName (name), error);
689 int n = WriteSegment (src, offset + copied, count);
702 delegate void WriteDelegate (byte [] buffer, int offset, int count);
704 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
705 AsyncCallback userCallback, object stateObject)
707 if (handle == MonoIO.InvalidHandle)
708 throw new ObjectDisposedException ("Stream has been closed");
711 throw new NotSupportedException ("This stream does not support writing");
714 throw new ArgumentNullException ("array");
717 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
720 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
722 // reordered to avoid possible integer overflow
723 if (numBytes > array.Length - offset)
724 throw new ArgumentException ("array too small. numBytes/offset wrong.");
727 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
729 FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
730 result.BytesRead = -1;
731 result.Count = numBytes;
732 result.OriginalCount = numBytes;
735 MemoryStream ms = new MemoryStream ();
737 ms.Write (array, offset, numBytes);
739 numBytes = (int) ms.Length;
742 WriteDelegate w = new WriteDelegate (WriteInternal);
743 return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
746 public override void EndWrite (IAsyncResult asyncResult)
748 if (asyncResult == null)
749 throw new ArgumentNullException ("asyncResult");
752 base.EndWrite (asyncResult);
756 AsyncResult ares = asyncResult as AsyncResult;
758 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
760 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
762 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
764 w.EndInvoke (asyncResult);
768 public override long Seek (long offset, SeekOrigin origin)
772 if (handle == MonoIO.InvalidHandle)
773 throw new ObjectDisposedException ("Stream has been closed");
777 if(CanSeek == false) {
778 throw new NotSupportedException("The stream does not support seeking");
783 pos = Length + offset;
786 case SeekOrigin.Current:
787 pos = Position + offset;
790 case SeekOrigin.Begin:
795 throw new ArgumentException ("origin", "Invalid SeekOrigin");
799 /* LAMESPEC: shouldn't this be
800 * ArgumentOutOfRangeException?
802 throw new IOException("Attempted to Seek before the beginning of the stream");
805 if(pos < this.append_startpos) {
806 /* More undocumented crap */
807 throw new IOException("Can't seek back over pre-existing data in append mode");
814 buf_start = MonoIO.Seek (handle, pos,
818 if (error != MonoIOError.ERROR_SUCCESS) {
819 // don't leak the path information for isolated storage
820 throw MonoIO.GetException (GetSecureFileName (name), error);
826 public override void SetLength (long value)
828 if (handle == MonoIO.InvalidHandle)
829 throw new ObjectDisposedException ("Stream has been closed");
832 throw new NotSupportedException("The stream does not support seeking");
834 if(CanWrite == false)
835 throw new NotSupportedException("The stream does not support writing");
838 throw new ArgumentOutOfRangeException("value is less than 0");
844 MonoIO.SetLength (handle, value, out error);
845 if (error != MonoIOError.ERROR_SUCCESS) {
846 // don't leak the path information for isolated storage
847 throw MonoIO.GetException (GetSecureFileName (name), error);
850 if (Position > value)
854 public override void Flush ()
856 if (handle == MonoIO.InvalidHandle)
857 throw new ObjectDisposedException ("Stream has been closed");
862 #if NET_4_0 || MOONLIGHT || MOBILE
863 public virtual void Flush (bool flushToDisk)
867 // This does the fsync
870 MonoIO.Flush (handle, out error);
875 public virtual void Lock (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");
885 if (handle == MonoIO.InvalidHandle) {
886 throw new ObjectDisposedException ("Stream has been closed");
891 MonoIO.Lock (handle, position, length, out error);
892 if (error != MonoIOError.ERROR_SUCCESS) {
893 // don't leak the path information for isolated storage
894 throw MonoIO.GetException (GetSecureFileName (name), error);
898 public virtual void Unlock (long position, long length)
900 if (handle == MonoIO.InvalidHandle)
901 throw new ObjectDisposedException ("Stream has been closed");
903 throw new ArgumentOutOfRangeException ("position must not be negative");
906 throw new ArgumentOutOfRangeException ("length must not be negative");
911 MonoIO.Unlock (handle, position, length, out error);
912 if (error != MonoIOError.ERROR_SUCCESS) {
913 // don't leak the path information for isolated storage
914 throw MonoIO.GetException (GetSecureFileName (name), error);
925 protected override void Dispose (bool disposing)
927 Exception exc = null;
928 if (handle != MonoIO.InvalidHandle) {
930 // If the FileStream is in "exposed" status
931 // it means that we do not have a buffer(we write the data without buffering)
932 // therefor we don't and can't flush the buffer becouse we don't have one.
934 } catch (Exception e) {
941 MonoIO.Close (handle, out error);
942 if (error != MonoIOError.ERROR_SUCCESS) {
943 // don't leak the path information for isolated storage
944 throw MonoIO.GetException (GetSecureFileName (name), error);
947 handle = MonoIO.InvalidHandle;
954 if (disposing && buf != null) {
955 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
956 lock (buf_recycle_lock) {
957 if (buf_recycle == null) {
964 GC.SuppressFinalize (this);
971 public FileSecurity GetAccessControl ()
973 return new FileSecurity (SafeFileHandle,
974 AccessControlSections.Owner |
975 AccessControlSections.Group |
976 AccessControlSections.Access);
979 public void SetAccessControl (FileSecurity fileSecurity)
981 if (null == fileSecurity)
982 throw new ArgumentNullException ("fileSecurity");
984 fileSecurity.PersistModifications (SafeFileHandle);
989 public override Task FlushAsync (CancellationToken cancellationToken)
991 return base.FlushAsync (cancellationToken);
994 public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
996 return base.ReadAsync (buffer, offset, count, cancellationToken);
999 public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
1001 return base.WriteAsync (buffer, offset, count, cancellationToken);
1007 // ReadSegment, WriteSegment, FlushBuffer,
1008 // RefillBuffer and ReadData should only be called
1009 // when the Monitor lock is held, but these methods
1010 // grab it again just to be safe.
1012 private int ReadSegment (byte [] dest, int dest_offset, int count)
1014 count = Math.Min (count, buf_length - buf_offset);
1017 // Use the fastest method, all range checks has been done
1018 Buffer.BlockCopyInternal (buf, buf_offset, dest, dest_offset, count);
1019 buf_offset += count;
1025 private int WriteSegment (byte [] src, int src_offset,
1028 if (count > buf_size - buf_offset) {
1029 count = buf_size - buf_offset;
1033 Buffer.BlockCopy (src, src_offset,
1036 buf_offset += count;
1037 if (buf_offset > buf_length) {
1038 buf_length = buf_offset;
1047 void FlushBuffer (Stream st)
1052 if (CanSeek == true && safeHandle == null) {
1053 MonoIO.Seek (handle, buf_start,
1056 if (error != MonoIOError.ERROR_SUCCESS) {
1057 // don't leak the path information for isolated storage
1058 throw MonoIO.GetException (GetSecureFileName (name), error);
1062 int wcount = buf_length;
1065 int n = MonoIO.Write (handle, buf, 0, buf_length, out error);
1066 if (error != MonoIOError.ERROR_SUCCESS) {
1067 // don't leak the path information for isolated storage
1068 throw MonoIO.GetException (GetSecureFileName (name), error);
1074 st.Write (buf, 0, buf_length);
1078 buf_start += buf_offset;
1079 buf_offset = buf_length = 0;
1083 private void FlushBuffer ()
1088 private void FlushBufferIfDirty ()
1094 private void RefillBuffer ()
1098 buf_length = ReadData (handle, buf, 0,
1102 private int ReadData (IntPtr handle, byte[] buf, int offset,
1108 /* when async == true, if we get here we don't suport AIO or it's disabled
1109 * and we're using the threadpool */
1110 amount = MonoIO.Read (handle, buf, offset, count, out error);
1111 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1112 amount = 0; // might not be needed, but well...
1113 } else if (error != MonoIOError.ERROR_SUCCESS) {
1114 // don't leak the path information for isolated storage
1115 throw MonoIO.GetException (GetSecureFileName (name), error);
1118 /* Check for read error */
1120 throw new IOException ();
1126 void InitBuffer (int size, bool isZeroSize)
1133 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1135 size = Math.Max (size, 8);
1138 // Instead of allocating a new default buffer use the
1139 // last one if there is any available
1141 if (size <= DefaultBufferSize && buf_recycle != null) {
1142 lock (buf_recycle_lock) {
1143 if (buf_recycle != null) {
1151 buf = new byte [size];
1153 Array.Clear (buf, 0, size);
1158 // buf_offset = buf_length = 0;
1159 // buf_dirty = false;
1162 private string GetSecureFileName (string filename)
1164 return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
1167 private string GetSecureFileName (string filename, bool full)
1169 return (anonymous) ? Path.GetFileName (filename) :
1170 (full) ? Path.GetFullPath (filename) : filename;
1175 internal const int DefaultBufferSize = 4096;
1177 // Input buffer ready for recycling
1178 static byte[] buf_recycle;
1179 static readonly object buf_recycle_lock = new object ();
1181 private byte [] buf; // the buffer
1182 private string name = "[Unknown]"; // name of file.
1184 SafeFileHandle safeHandle; // set only when using one of the
1185 // constructors taking SafeFileHandle
1187 private long append_startpos;
1188 IntPtr handle; // handle to underlying file
1190 private FileAccess access;
1193 private bool canseek;
1194 private bool anonymous;
1195 private bool buf_dirty; // true if buffer has been written to
1197 private int buf_size; // capacity in bytes
1198 private int buf_length; // number of valid bytes in buffer
1199 private int buf_offset; // position of next byte
1200 private long buf_start; // location of buffer in file