Merge pull request #799 from kebby/master
[mono.git] / mcs / class / corlib / System.IO / FileStream.cs
1 //
2 // System.IO.FileStream.cs
3 //
4 // Authors:
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)
9 //
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).
13 //
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:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
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.
32 //
33
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;
43
44 #if NET_2_1
45 using System.IO.IsolatedStorage;
46 #else
47 using System.Security.AccessControl;
48 #endif
49
50 #if NET_4_5
51 using System.Threading.Tasks;
52 #endif
53
54 namespace System.IO
55 {
56         [ComVisible (true)]
57         public class FileStream : Stream
58         {
59                 // construct from handle
60                 
61                 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
62                 public FileStream (IntPtr handle, FileAccess access)
63                         : this (handle, access, true, DefaultBufferSize, false) {}
64
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) {}
68                 
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) {}
72
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) {}
76
77                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
78                 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isZeroSize)
79                 {
80                         this.handle = MonoIO.InvalidHandle;
81                         if (handle == this.handle)
82                                 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
83
84                         if (access < FileAccess.Read || access > FileAccess.ReadWrite)
85                                 throw new ArgumentOutOfRangeException ("access");
86
87                         MonoIOError error;
88                         MonoFileType ftype = MonoIO.GetFileType (handle, out error);
89
90                         if (error != MonoIOError.ERROR_SUCCESS) {
91                                 throw MonoIO.GetException (name, error);
92                         }
93                         
94                         if (ftype == MonoFileType.Unknown) {
95                                 throw new IOException ("Invalid handle.");
96                         } else if (ftype == MonoFileType.Disk) {
97                                 this.canseek = true;
98                         } else {
99                                 this.canseek = false;
100                         }
101
102                         this.handle = handle;
103                         ExposeHandle ();
104                         this.access = access;
105                         this.owner = ownsHandle;
106                         this.async = isAsync;
107                         this.anonymous = false;
108                         if (canseek) {
109                                 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
110                                 if (error != MonoIOError.ERROR_SUCCESS) {
111                                         throw MonoIO.GetException (name, error);
112                                 }
113                         }
114
115                         /* Can't set append mode */
116                         this.append_startpos=0;
117                 }
118
119                 // construct from filename
120                 
121                 public FileStream (string path, FileMode mode)
122                         : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
123                 {
124                 }
125
126                 public FileStream (string path, FileMode mode, FileAccess access)
127                         : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
128                 {
129                 }
130
131                 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
132                         : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
133                 {
134                 }
135
136                 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
137                         : this (path, mode, access, share, bufferSize, false, FileOptions.None)
138                 {
139                 }
140
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)
143                 {
144                 }
145
146                 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
147                         : this (path, mode, access, share, bufferSize, false, options)
148                 {
149                 }
150
151 #if !NET_2_1
152                 public FileStream (SafeFileHandle handle, FileAccess access)
153                         :this(handle, access, DefaultBufferSize, false)
154                 {
155                 }
156                 
157                 public FileStream (SafeFileHandle handle, FileAccess access,
158                                    int bufferSize)
159                         :this(handle, access, bufferSize, false)
160                 {
161                 }
162                 
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)
167                 {
168                         this.safeHandle = handle;
169                 }
170
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)
176                 {
177                 }
178                 
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)
185                 {
186                 }
187 #endif
188
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)
191                 {
192                 }
193
194                 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
195                 {
196                         if (path == null) {
197                                 throw new ArgumentNullException ("path");
198                         }
199
200                         if (path.Length == 0) {
201                                 throw new ArgumentException ("Path is empty");
202                         }
203
204                         this.anonymous = anonymous;
205                         // ignore the Inheritable flag
206                         share &= ~FileShare.Inheritable;
207
208                         if (bufferSize <= 0)
209                                 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
210
211                         if (mode < FileMode.CreateNew || mode > FileMode.Append) {
212 #if NET_2_1
213                                 if (anonymous)
214                                         throw new ArgumentException ("mode", "Enum value was out of legal range.");
215                                 else
216 #endif
217                                 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
218                         }
219
220                         if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
221                                 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
222                         }
223
224                         if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
225                                 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
226                         }
227
228                         if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
229                                 throw new ArgumentException ("Name has invalid chars");
230                         }
231
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)));
236                         }
237
238                         /* Append streams can't be read (see FileMode
239                          * docs)
240                          */
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.");
244                         }
245
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));
251                         }
252
253                         SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
254
255                         string dname;
256                         if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
257                                 dname = Path.GetDirectoryName (Path.GetFullPath (path));
258                         else
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));
267                                 }
268                         }
269
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);
276                         }
277
278                         // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
279                         if (!anonymous)
280                                 this.name = path;
281
282                         // TODO: demand permissions
283
284                         MonoIOError error;
285
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);
290                         }
291
292                         this.access = access;
293                         this.owner = true;
294
295                         /* Can we open non-files by name? */
296                         
297                         if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
298                                 this.canseek = true;
299                                 this.async = (options & FileOptions.Asynchronous) != 0;
300                         } else {
301                                 this.canseek = false;
302                                 this.async = false;
303                         }
304
305
306                         if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
307                                 /* Avoid allocating a large buffer for small files */
308                                 long len = Length;
309                                 if (bufferSize > len) {
310                                         bufferSize = (int)(len < 1000 ? 1000 : len);
311                                 }
312                         }
313
314                         InitBuffer (bufferSize, false);
315
316                         if (mode==FileMode.Append) {
317                                 this.Seek (0, SeekOrigin.End);
318                                 this.append_startpos=this.Position;
319                         } else {
320                                 this.append_startpos=0;
321                         }
322                 }
323
324                 // properties
325                 
326                 public override bool CanRead {
327                         get {
328                                 return access == FileAccess.Read ||
329                                        access == FileAccess.ReadWrite;
330                         }
331                 }
332
333                 public override bool CanWrite {
334                         get {
335                                 return access == FileAccess.Write ||
336                                         access == FileAccess.ReadWrite;
337                         }
338                 }
339                 
340                 public override bool CanSeek {
341                         get {
342                                 return(canseek);
343                         }
344                 }
345
346                 public virtual bool IsAsync {
347                         get {
348                                 return (async);
349                         }
350                 }
351
352                 public string Name {
353                         get {
354                                 return name;
355                         }
356                 }
357
358                 public override long Length {
359                         get {
360                                 if (handle == MonoIO.InvalidHandle)
361                                         throw new ObjectDisposedException ("Stream has been closed");
362
363                                 if (!CanSeek)
364                                         throw new NotSupportedException ("The stream does not support seeking");
365
366                                 // Buffered data might change the length of the stream
367                                 FlushBufferIfDirty ();
368
369                                 MonoIOError error;
370                                 long length;
371                                 
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);
376                                 }
377
378                                 return(length);
379                         }
380                 }
381
382                 public override long Position {
383                         get {
384                                 if (handle == MonoIO.InvalidHandle)
385                                         throw new ObjectDisposedException ("Stream has been closed");
386
387                                 if (CanSeek == false)
388                                         throw new NotSupportedException("The stream does not support seeking");
389
390                                 if (safeHandle != null) {
391                                         // If the handle was leaked outside we always ask the real handle
392                                         MonoIOError error;
393
394                                         long ret = MonoIO.Seek (handle, 0,SeekOrigin.Current,out error);
395
396                                         if (error != MonoIOError.ERROR_SUCCESS) {
397                                                 // don't leak the path information for isolated storage
398                                                 throw MonoIO.GetException (GetSecureFileName (name), error);
399                                         }
400
401                                         return ret;
402                                 }
403         
404                                 return(buf_start + buf_offset);
405                         }
406                         set {
407                                 if(value < 0) {
408                                         throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
409                                 }
410                                 
411                                 Seek (value, SeekOrigin.Begin);
412                         }
413                 }
414
415                 [Obsolete ("Use SafeFileHandle instead")]
416                 public virtual IntPtr Handle {
417                         [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
418                         [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
419                         get {
420                                 if (safeHandle == null) {
421                                         ExposeHandle ();
422                                 }
423                                 return handle;
424                         }
425                 }
426
427                 public virtual SafeFileHandle SafeFileHandle {
428                         [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
429                         [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
430                         get {
431                                 if (safeHandle == null) {
432                                         ExposeHandle ();
433                                 }
434                                 return safeHandle;
435                         }
436                 }
437
438                 // methods
439
440           void ExposeHandle ()
441                 {
442                         safeHandle = new SafeFileHandle (handle, false);
443                         FlushBuffer ();
444                         InitBuffer (0, true);
445                 }
446
447                 public override int ReadByte ()
448                 {
449                         if (handle == MonoIO.InvalidHandle)
450                                 throw new ObjectDisposedException ("Stream has been closed");
451
452                         if (!CanRead)
453                                 throw new NotSupportedException ("Stream does not support reading");
454                         
455                         if (buf_size == 0) {
456                                 int n = ReadData (handle, buf, 0, 1);
457                                 if (n == 0) return -1;
458                                 else return buf[0];
459                         }
460                         else if (buf_offset >= buf_length) {
461                                 RefillBuffer ();
462
463                                 if (buf_length == 0)
464                                         return -1;
465                         }
466                         
467                         return buf [buf_offset ++];
468                 }
469
470                 public override void WriteByte (byte value)
471                 {
472                         if (handle == MonoIO.InvalidHandle)
473                                 throw new ObjectDisposedException ("Stream has been closed");
474
475                         if (!CanWrite)
476                                 throw new NotSupportedException ("Stream does not support writing");
477
478                         if (buf_offset == buf_size)
479                                 FlushBuffer ();
480
481                         if (buf_size == 0) { // No buffering
482                                 buf [0] = value;
483                                 buf_dirty = true;
484                                 buf_length = 1;
485                                 FlushBuffer ();
486                                 return;
487                         }
488
489                         buf [buf_offset ++] = value;
490                         if (buf_offset > buf_length)
491                                 buf_length = buf_offset;
492
493                         buf_dirty = true;
494                 }
495
496                 public override int Read ([In,Out] byte[] array, int offset, int count)
497                 {
498                         if (handle == MonoIO.InvalidHandle)
499                                 throw new ObjectDisposedException ("Stream has been closed");
500                         if (array == null)
501                                 throw new ArgumentNullException ("array");
502                         if (!CanRead)
503                                 throw new NotSupportedException ("Stream does not support reading");
504                         int len = array.Length;
505                         if (offset < 0)
506                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
507                         if (count < 0)
508                                 throw new ArgumentOutOfRangeException ("count", "< 0");
509                         if (offset > len)
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");
514
515                         if (async) {
516                                 IAsyncResult ares = BeginRead (array, offset, count, null, null);
517                                 return EndRead (ares);
518                         }
519
520                         return ReadInternal (array, offset, count);
521                 }
522
523                 int ReadInternal (byte [] dest, int offset, int count)
524                 {
525                         int n = ReadSegment (dest, offset, count);
526                         if (n == count) {
527                                 return count;
528                         }
529                         
530                         int copied = n;
531                         count -= n;
532                         if (count > buf_size) {
533                                 /* Read as much as we can, up
534                                  * to count bytes
535                                  */
536                                 FlushBuffer();
537                                 n = ReadData (handle, dest,
538                                               offset+n,
539                                               count);
540                         
541                                 /* Make the next buffer read
542                                  * start from the right place
543                                  */
544                                 buf_start += n;
545                         } else {
546                                 RefillBuffer ();
547                                 n = ReadSegment (dest,
548                                                  offset+copied,
549                                                  count);
550                         }
551
552                         return copied + n;
553                 }
554
555                 delegate int ReadDelegate (byte [] buffer, int offset, int count);
556
557                 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
558                                                         AsyncCallback userCallback, object stateObject)
559                 {
560                         if (handle == MonoIO.InvalidHandle)
561                                 throw new ObjectDisposedException ("Stream has been closed");
562
563                         if (!CanRead)
564                                 throw new NotSupportedException ("This stream does not support reading");
565
566                         if (array == null)
567                                 throw new ArgumentNullException ("array");
568
569                         if (numBytes < 0)
570                                 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
571
572                         if (offset < 0)
573                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
574
575                         // reordered to avoid possible integer overflow
576                         if (numBytes > array.Length - offset)
577                                 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
578
579                         if (!async)
580                                 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
581
582                         ReadDelegate r = new ReadDelegate (ReadInternal);
583                         return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
584                 }
585                 
586                 public override int EndRead (IAsyncResult asyncResult)
587                 {
588                         if (asyncResult == null)
589                                 throw new ArgumentNullException ("asyncResult");
590
591                         if (!async)
592                                 return base.EndRead (asyncResult);
593
594                         AsyncResult ares = asyncResult as AsyncResult;
595                         if (ares == null)
596                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
597
598                         ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
599                         if (r == null)
600                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
601
602                         return r.EndInvoke (asyncResult);
603                 }
604
605                 public override void Write (byte[] array, int offset, int count)
606                 {
607                         if (handle == MonoIO.InvalidHandle)
608                                 throw new ObjectDisposedException ("Stream has been closed");
609                         if (array == null)
610                                 throw new ArgumentNullException ("array");
611                         if (offset < 0)
612                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
613                         if (count < 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");
618                         if (!CanWrite)
619                                 throw new NotSupportedException ("Stream does not support writing");
620
621                         if (async) {
622                                 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
623                                 EndWrite (ares);
624                                 return;
625                         }
626
627                         WriteInternal (array, offset, count);
628                 }
629
630                 void WriteInternal (byte [] src, int offset, int count)
631                 {
632                         if (count > buf_size) {
633                                 // shortcut for long writes
634                                 MonoIOError error;
635
636                                 FlushBuffer ();
637                                 int wcount = count;
638                                 
639                                 while (wcount > 0){
640                                         int n = MonoIO.Write (handle, src, offset, wcount, out error);
641                                         if (error != MonoIOError.ERROR_SUCCESS)
642                                                 throw MonoIO.GetException (GetSecureFileName (name), error);
643                                         
644                                         wcount -= n;
645                                         offset += n;
646                                 } 
647                                 buf_start += count;
648                         } else {
649
650                                 int copied = 0;
651                                 while (count > 0) {
652                                         
653                                         int n = WriteSegment (src, offset + copied, count);
654                                         copied += n;
655                                         count -= n;
656
657                                         if (count == 0) {
658                                                 break;
659                                         }
660
661                                         FlushBuffer ();
662                                 }
663                         }
664                 }
665
666                 delegate void WriteDelegate (byte [] buffer, int offset, int count);
667
668                 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
669                                                         AsyncCallback userCallback, object stateObject)
670                 {
671                         if (handle == MonoIO.InvalidHandle)
672                                 throw new ObjectDisposedException ("Stream has been closed");
673
674                         if (!CanWrite)
675                                 throw new NotSupportedException ("This stream does not support writing");
676
677                         if (array == null)
678                                 throw new ArgumentNullException ("array");
679
680                         if (numBytes < 0)
681                                 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
682
683                         if (offset < 0)
684                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
685
686                         // reordered to avoid possible integer overflow
687                         if (numBytes > array.Length - offset)
688                                 throw new ArgumentException ("array too small. numBytes/offset wrong.");
689
690                         if (!async)
691                                 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
692
693                         FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
694                         result.BytesRead = -1;
695                         result.Count = numBytes;
696                         result.OriginalCount = numBytes;
697
698                         if (buf_dirty) {
699                                 MemoryStream ms = new MemoryStream ();
700                                 FlushBuffer (ms);
701                                 ms.Write (array, offset, numBytes);
702
703                                 // Set arguments to new compounded buffer 
704                                 offset = 0;
705                                 array = ms.ToArray ();
706                                 numBytes = array.Length;
707                         }
708
709                         WriteDelegate w = WriteInternal;
710                         return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);                      
711                 }
712                 
713                 public override void EndWrite (IAsyncResult asyncResult)
714                 {
715                         if (asyncResult == null)
716                                 throw new ArgumentNullException ("asyncResult");
717
718                         if (!async) {
719                                 base.EndWrite (asyncResult);
720                                 return;
721                         }
722
723                         AsyncResult ares = asyncResult as AsyncResult;
724                         if (ares == null)
725                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
726
727                         WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
728                         if (w == null)
729                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
730
731                         w.EndInvoke (asyncResult);
732                         return;
733                 }
734
735                 public override long Seek (long offset, SeekOrigin origin)
736                 {
737                         long pos;
738
739                         if (handle == MonoIO.InvalidHandle)
740                                 throw new ObjectDisposedException ("Stream has been closed");
741                         
742                         // make absolute
743
744                         if(CanSeek == false) {
745                                 throw new NotSupportedException("The stream does not support seeking");
746                         }
747
748                         switch (origin) {
749                         case SeekOrigin.End:
750                                 pos = Length + offset;
751                                 break;
752
753                         case SeekOrigin.Current:
754                                 pos = Position + offset;
755                                 break;
756
757                         case SeekOrigin.Begin:
758                                 pos = offset;
759                                 break;
760
761                         default:
762                                 throw new ArgumentException ("origin", "Invalid SeekOrigin");
763                         }
764
765                         if (pos < 0) {
766                                 /* LAMESPEC: shouldn't this be
767                                  * ArgumentOutOfRangeException?
768                                  */
769                                 throw new IOException("Attempted to Seek before the beginning of the stream");
770                         }
771
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");
775                         }
776
777                         FlushBuffer ();
778
779                         MonoIOError error;
780                 
781                         buf_start = MonoIO.Seek (handle, pos,
782                                                  SeekOrigin.Begin,
783                                                  out error);
784
785                         if (error != MonoIOError.ERROR_SUCCESS) {
786                                 // don't leak the path information for isolated storage
787                                 throw MonoIO.GetException (GetSecureFileName (name), error);
788                         }
789                         
790                         return(buf_start);
791                 }
792
793                 public override void SetLength (long value)
794                 {
795                         if (handle == MonoIO.InvalidHandle)
796                                 throw new ObjectDisposedException ("Stream has been closed");
797
798                         if(CanSeek == false)
799                                 throw new NotSupportedException("The stream does not support seeking");
800
801                         if(CanWrite == false)
802                                 throw new NotSupportedException("The stream does not support writing");
803
804                         if(value < 0)
805                                 throw new ArgumentOutOfRangeException("value is less than 0");
806                         
807                         Flush ();
808
809                         MonoIOError error;
810                         
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);
815                         }
816
817                         if (Position > value)
818                                 Position = value;
819                 }
820
821                 public override void Flush ()
822                 {
823                         if (handle == MonoIO.InvalidHandle)
824                                 throw new ObjectDisposedException ("Stream has been closed");
825
826                         FlushBuffer ();
827                 }
828
829 #if NET_4_0
830                 public virtual void Flush (bool flushToDisk)
831                 {
832                         FlushBuffer ();
833
834                         // This does the fsync
835                         if (flushToDisk){
836                                 MonoIOError error;
837                                 MonoIO.Flush (handle, out error);
838                         }
839                 }
840 #endif
841
842                 public virtual void Lock (long position, long length)
843                 {
844                         if (handle == MonoIO.InvalidHandle)
845                                 throw new ObjectDisposedException ("Stream has been closed");
846                         if (position < 0) {
847                                 throw new ArgumentOutOfRangeException ("position must not be negative");
848                         }
849                         if (length < 0) {
850                                 throw new ArgumentOutOfRangeException ("length must not be negative");
851                         }
852                         if (handle == MonoIO.InvalidHandle) {
853                                 throw new ObjectDisposedException ("Stream has been closed");
854                         }
855                                 
856                         MonoIOError error;
857
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);
862                         }
863                 }
864
865                 public virtual void Unlock (long position, long length)
866                 {
867                         if (handle == MonoIO.InvalidHandle)
868                                 throw new ObjectDisposedException ("Stream has been closed");
869                         if (position < 0) {
870                                 throw new ArgumentOutOfRangeException ("position must not be negative");
871                         }
872                         if (length < 0) {
873                                 throw new ArgumentOutOfRangeException ("length must not be negative");
874                         }
875                                 
876                         MonoIOError error;
877
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);
882                         }
883                 }
884
885                 // protected
886
887                 ~FileStream ()
888                 {
889                         Dispose (false);
890                 }
891
892                 protected override void Dispose (bool disposing)
893                 {
894                         Exception exc = null;
895                         if (handle != MonoIO.InvalidHandle) {
896                                 try {
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.
900                                         FlushBuffer ();
901                                 } catch (Exception e) {
902                                         exc = e;
903                                 }
904
905                                 if (owner) {
906                                         MonoIOError error;
907                                 
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);
912                                         }
913
914                                         handle = MonoIO.InvalidHandle;
915                                 }
916                         }
917
918                         canseek = false;
919                         access = 0;
920                         
921                         if (disposing && buf != null) {
922                                 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
923                                         lock (buf_recycle_lock) {
924                                                 if (buf_recycle == null) {
925                                                         buf_recycle = buf;
926                                                 }
927                                         }
928                                 }
929                                 
930                                 buf = null;
931                                 GC.SuppressFinalize (this);
932                         }
933                         if (exc != null)
934                                 throw exc;
935                 }
936
937 #if !NET_2_1
938                 public FileSecurity GetAccessControl ()
939                 {
940                         return new FileSecurity (SafeFileHandle,
941                                                  AccessControlSections.Owner |
942                                                  AccessControlSections.Group |
943                                                  AccessControlSections.Access);
944                 }
945                 
946                 public void SetAccessControl (FileSecurity fileSecurity)
947                 {
948                         if (null == fileSecurity)
949                                 throw new ArgumentNullException ("fileSecurity");
950                                 
951                         fileSecurity.PersistModifications (SafeFileHandle);
952                 }
953 #endif
954
955 #if NET_4_5
956                 public override Task FlushAsync (CancellationToken cancellationToken)
957                 {
958                         return base.FlushAsync (cancellationToken);
959                 }
960
961                 public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
962                 {
963                         return base.ReadAsync (buffer, offset, count, cancellationToken);
964                 }
965
966                 public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
967                 {
968                         return base.WriteAsync (buffer, offset, count, cancellationToken);
969                 }
970 #endif
971
972                 // private.
973
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.
978
979                 private int ReadSegment (byte [] dest, int dest_offset, int count)
980                 {
981                         count = Math.Min (count, buf_length - buf_offset);
982                         
983                         if (count > 0) {
984                                 // Use the fastest method, all range checks has been done
985                                 Buffer.BlockCopyInternal (buf, buf_offset, dest, dest_offset, count);
986                                 buf_offset += count;
987                         }
988                         
989                         return count;
990                 }
991
992                 private int WriteSegment (byte [] src, int src_offset,
993                                           int count)
994                 {
995                         if (count > buf_size - buf_offset) {
996                                 count = buf_size - buf_offset;
997                         }
998                         
999                         if (count > 0) {
1000                                 Buffer.BlockCopy (src, src_offset,
1001                                                   buf, buf_offset,
1002                                                   count);
1003                                 buf_offset += count;
1004                                 if (buf_offset > buf_length) {
1005                                         buf_length = buf_offset;
1006                                 }
1007                                 
1008                                 buf_dirty = true;
1009                         }
1010                         
1011                         return(count);
1012                 }
1013
1014                 void FlushBuffer (Stream st)
1015                 {
1016                         if (buf_dirty) {
1017                                 MonoIOError error;
1018
1019                                 if (CanSeek == true && safeHandle == null) {
1020                                         MonoIO.Seek (handle, buf_start,
1021                                                      SeekOrigin.Begin,
1022                                                      out error);
1023                                         if (error != MonoIOError.ERROR_SUCCESS) {
1024                                                 // don't leak the path information for isolated storage
1025                                                 throw MonoIO.GetException (GetSecureFileName (name), error);
1026                                         }
1027                                 }
1028                                 if (st == null) {
1029                                         int wcount = buf_length;
1030                                         int offset = 0;
1031                                         while (wcount > 0){
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);
1036                                                 }
1037                                                 wcount -= n;
1038                                                 offset += n;
1039                                         }
1040                                 } else {
1041                                         st.Write (buf, 0, buf_length);
1042                                 }
1043                         }
1044
1045                         buf_start += buf_offset;
1046                         buf_offset = buf_length = 0;
1047                         buf_dirty = false;
1048                 }
1049
1050                 private void FlushBuffer ()
1051                 {
1052                         FlushBuffer (null);
1053                 }
1054
1055                 private void FlushBufferIfDirty ()
1056                 {
1057                         if (buf_dirty)
1058                                 FlushBuffer (null);
1059                 }
1060
1061                 private void RefillBuffer ()
1062                 {
1063                         FlushBuffer (null);
1064                         
1065                         buf_length = ReadData (handle, buf, 0,
1066                                                buf_size);
1067                 }
1068
1069                 private int ReadData (IntPtr handle, byte[] buf, int offset,
1070                                       int count)
1071                 {
1072                         MonoIOError error;
1073                         int amount = 0;
1074
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);
1083                         }
1084                         
1085                         /* Check for read error */
1086                         if(amount == -1) {
1087                                 throw new IOException ();
1088                         }
1089                         
1090                         return(amount);
1091                 }
1092                                 
1093                 void InitBuffer (int size, bool isZeroSize)
1094                 {
1095                         if (isZeroSize) {
1096                                 size = 0;
1097                                 buf = new byte[1];
1098                         } else {
1099                                 if (size <= 0)
1100                                         throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1101
1102                                 size = Math.Max (size, 8);
1103
1104                                 //
1105                                 // Instead of allocating a new default buffer use the
1106                                 // last one if there is any available
1107                                 //              
1108                                 if (size <= DefaultBufferSize && buf_recycle != null) {
1109                                         lock (buf_recycle_lock) {
1110                                                 if (buf_recycle != null) {
1111                                                         buf = buf_recycle;
1112                                                         buf_recycle = null;
1113                                                 }
1114                                         }
1115                                 }
1116
1117                                 if (buf == null)
1118                                         buf = new byte [size];
1119                                 else
1120                                         Array.Clear (buf, 0, size);
1121                         }
1122                                         
1123                         buf_size = size;
1124 //                      buf_start = 0;
1125 //                      buf_offset = buf_length = 0;
1126 //                      buf_dirty = false;
1127                 }
1128
1129                 private string GetSecureFileName (string filename)
1130                 {
1131                         return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
1132                 }
1133
1134                 private string GetSecureFileName (string filename, bool full)
1135                 {
1136                         return (anonymous) ? Path.GetFileName (filename) : 
1137                                 (full) ? Path.GetFullPath (filename) : filename;
1138                 }
1139
1140                 // fields
1141
1142                 internal const int DefaultBufferSize = 4096;
1143
1144                 // Input buffer ready for recycling                             
1145                 static byte[] buf_recycle;
1146                 static readonly object buf_recycle_lock = new object ();
1147
1148                 private byte [] buf;                    // the buffer
1149                 private string name = "[Unknown]";      // name of file.
1150
1151                 SafeFileHandle safeHandle;              // set only when using one of the
1152                                                         // constructors taking SafeFileHandle
1153
1154                 private long append_startpos;
1155                 IntPtr handle;                          // handle to underlying file
1156
1157                 private FileAccess access;
1158                 private bool owner;
1159                 private bool async;
1160                 private bool canseek;
1161                 private bool anonymous;
1162                 private bool buf_dirty;                 // true if buffer has been written to
1163
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
1168         }
1169 }