merge -r 58060:58217
[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 //
9 // (C) 2001-2003 Ximian, Inc.  http://www.ximian.com
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections;
33 using System.Globalization;
34 using System.Runtime.CompilerServices;
35 using System.Runtime.InteropServices;
36 using System.Runtime.Remoting.Messaging;
37 using System.Security.Permissions;
38 using System.Threading;
39
40 #if NET_2_0
41 using Microsoft.Win32.SafeHandles;
42 #endif
43
44 namespace System.IO
45 {
46         public class FileStream : Stream
47         {
48                 // construct from handle
49                 
50                 public FileStream (IntPtr handle, FileAccess access)
51                         : this (handle, access, true, DefaultBufferSize, false) {}
52
53                 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
54                         : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
55                 
56                 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
57                         : this (handle, access, ownsHandle, bufferSize, false) {}
58
59                 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
60                         : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
61
62                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
63                 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool noBuffering)
64                 {
65                         this.handle = MonoIO.InvalidHandle;
66                         if (handle == this.handle)
67                                 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
68
69                         if (access < FileAccess.Read || access > FileAccess.ReadWrite)
70                                 throw new ArgumentOutOfRangeException ("access");
71
72                         MonoIOError error;
73                         MonoFileType ftype = MonoIO.GetFileType (handle, out error);
74
75                         if (error != MonoIOError.ERROR_SUCCESS) {
76                                 throw MonoIO.GetException (name, error);
77                         }
78                         
79                         if (ftype == MonoFileType.Unknown) {
80                                 throw new IOException ("Invalid handle.");
81                         } else if (ftype == MonoFileType.Disk) {
82                                 this.canseek = true;
83                         } else {
84                                 this.canseek = false;
85                                 noBuffering = true;
86                                 bufferSize = 0;
87                         }
88
89                         this.handle = handle;
90                         this.access = access;
91                         this.owner = ownsHandle;
92                         this.async = isAsync;
93                         this.anonymous = false;
94
95                         InitBuffer (bufferSize, noBuffering);
96
97                         /* Can't set append mode */
98                         this.append_startpos=0;
99                 }
100
101                 // construct from filename
102                 
103                 public FileStream (string name, FileMode mode)
104                         : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, false)
105                 {
106                 }
107
108                 public FileStream (string name, FileMode mode, FileAccess access)
109                         : this (name, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
110                 {
111                 }
112
113                 public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
114                         : this (name, mode, access, share, DefaultBufferSize, false, false)
115                 {
116                 }
117                 
118                 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
119                         : this (name, mode, access, share, bufferSize, false, false)
120                 {
121                 }
122
123                 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
124                         : this (name, mode, access, share, bufferSize, isAsync, false)
125                 {
126                 }
127
128                 internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
129                 {
130                         if (name == null) {
131                                 throw new ArgumentNullException ("name");
132                         }
133                         
134                         if (name.Length == 0) {
135                                 throw new ArgumentException ("Name is empty");
136                         }
137
138                         if (bufferSize <= 0)
139                                 throw new ArgumentOutOfRangeException ("Positive number required.");
140
141                         if (mode < FileMode.CreateNew || mode > FileMode.Append)
142                                 throw new ArgumentOutOfRangeException ("mode");
143
144                         if (access < FileAccess.Read || access > FileAccess.ReadWrite)
145                                 throw new ArgumentOutOfRangeException ("access");
146
147                         if (share < FileShare.None || share > FileShare.ReadWrite)
148                                 throw new ArgumentOutOfRangeException ("share");
149
150                         if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
151                                 throw new ArgumentException ("Name has invalid chars");
152                         }
153
154                         if (Directory.Exists (name)) {
155                                 // don't leak the path information for isolated storage
156                                 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
157                                 string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
158                                 throw new UnauthorizedAccessException (String.Format (msg, fname));
159                         }
160
161                         /* Append streams can't be read (see FileMode
162                          * docs)
163                          */
164                         if (mode==FileMode.Append &&
165                             (access&FileAccess.Read)==FileAccess.Read) {
166                                 throw new ArgumentException("Append streams can not be read");
167                         }
168
169                         if ((access & FileAccess.Write) == 0 &&
170                             (mode != FileMode.Open && mode != FileMode.OpenOrCreate))
171                                 throw new ArgumentException ("access and mode not compatible");
172
173                         string dname = Path.GetDirectoryName (name);
174                         if (dname.Length > 0) {
175                                 string fp = Path.GetFullPath (dname);
176                                 if (!Directory.Exists (fp)) {
177                                         // don't leak the path information for isolated storage
178                                         string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
179                                         string fname = (anonymous) ? dname : fp;
180                                         throw new DirectoryNotFoundException (String.Format (msg, fname));
181                                 }
182                         }
183
184                         if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
185                                         mode != FileMode.CreateNew && !File.Exists (name)) {
186                                 // don't leak the path information for isolated storage
187                                 string msg = Locale.GetText ("Could not find file \"{0}\".");
188                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
189                                 throw new FileNotFoundException (String.Format (msg, fname), fname);
190                         }
191
192                         // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
193                         if (!anonymous)
194                                 this.name = name;
195
196                         // TODO: demand permissions
197
198                         MonoIOError error;
199
200                         this.handle = MonoIO.Open (name, mode, access, share, false, out error);
201                         if (handle == MonoIO.InvalidHandle) {
202                                 // don't leak the path information for isolated storage
203                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
204                                 throw MonoIO.GetException (fname, error);
205                         }
206
207                         this.access = access;
208                         this.owner = true;
209                         this.anonymous = anonymous;
210
211                         /* Can we open non-files by name? */
212                         
213                         if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
214                                 this.canseek = true;
215                                 this.async = isAsync;
216                         } else {
217                                 this.canseek = false;
218                                 this.async = false;
219                         }
220
221
222                         if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
223                                 /* Avoid allocating a large buffer for small files */
224                                 long len = Length;
225                                 if (bufferSize > len) {
226                                         bufferSize = (int)(len < 1000 ? 1000 : len);
227                                 }
228                         }
229
230                         InitBuffer (bufferSize, false);
231
232                         if (mode==FileMode.Append) {
233                                 this.Seek (0, SeekOrigin.End);
234                                 this.append_startpos=this.Position;
235                         } else {
236                                 this.append_startpos=0;
237                         }
238                 }
239
240                 // properties
241                 
242                 public override bool CanRead {
243                         get {
244                                 return access == FileAccess.Read ||
245                                        access == FileAccess.ReadWrite;
246                         }
247                 }
248
249                 public override bool CanWrite {
250                         get {
251                                 return access == FileAccess.Write ||
252                                        access == FileAccess.ReadWrite;
253                         }
254                 }
255                 
256                 public override bool CanSeek {
257                         get {
258                                 return(canseek);
259                         }
260                 }
261
262                 public virtual bool IsAsync {
263                         get {
264                                 return (async);
265                         }
266                 }
267
268                 public string Name {
269                         get {
270                                 return name; 
271                         }
272                 }
273
274                 public override long Length {
275                         get {
276                                 if (handle == MonoIO.InvalidHandle)
277                                         throw new ObjectDisposedException ("Stream has been closed");
278
279                                 if (!CanSeek)
280                                         throw new NotSupportedException ("The stream does not support seeking");
281
282                                 // Buffered data might change the length of the stream
283                                 FlushBufferIfDirty ();
284
285                                 MonoIOError error;
286                                 long length;
287                                 
288                                 length = MonoIO.GetLength (handle, out error);
289                                 if (error != MonoIOError.ERROR_SUCCESS) {
290                                         // don't leak the path information for isolated storage
291                                         string fname = (anonymous) ? Path.GetFileName (name) : name;
292                                         throw MonoIO.GetException (fname, error);
293                                 }
294
295                                 return(length);
296                         }
297                 }
298
299                 public override long Position {
300                         get {
301                                 if (handle == MonoIO.InvalidHandle)
302                                         throw new ObjectDisposedException ("Stream has been closed");
303
304                                 if(CanSeek == false)
305                                         throw new NotSupportedException("The stream does not support seeking");
306                                 
307                                 return(buf_start + buf_offset);
308                         }
309                         set {
310                                 if (handle == MonoIO.InvalidHandle)
311                                         throw new ObjectDisposedException ("Stream has been closed");
312
313                                 if(CanSeek == false) {
314                                         throw new NotSupportedException("The stream does not support seeking");
315                                 }
316
317                                 if(value < 0) {
318                                         throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
319                                 }
320                                 
321                                 Seek (value, SeekOrigin.Begin);
322                         }
323                 }
324
325                 public virtual IntPtr Handle {
326                         [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
327                         [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
328                         get {
329                                 return handle;
330                         }
331                 }
332
333 #if NET_2_0
334                 public virtual SafeFileHandle SafeFileHandle {
335                         [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
336                         [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
337                         get { throw new NotImplementedException (); }
338                 }
339 #endif
340
341                 // methods
342
343                 public override int ReadByte ()
344                 {
345                         if (handle == MonoIO.InvalidHandle)
346                                 throw new ObjectDisposedException ("Stream has been closed");
347
348                         if (!CanRead)
349                                 throw new NotSupportedException ("Stream does not support reading");
350                         
351                         if (buf_size == 0) {
352                                 int n = ReadData (handle, buf, 0, 1);
353                                 if (n == 0) return -1;
354                                 else return buf[0];
355                         }
356                         else if (buf_offset >= buf_length) {
357                                 RefillBuffer ();
358
359                                 if (buf_length == 0)
360                                         return -1;
361                         }
362                         
363                         return buf [buf_offset ++];
364                 }
365
366                 public override void WriteByte (byte value)
367                 {
368                         if (handle == MonoIO.InvalidHandle)
369                                 throw new ObjectDisposedException ("Stream has been closed");
370
371                         if (!CanWrite)
372                                 throw new NotSupportedException ("Stream does not support writing");
373
374                         if (buf_offset == buf_size)
375                                 FlushBuffer ();
376
377                         if (buf_size == 0) { // No buffering
378                                 buf [0] = value;
379                                 buf_dirty = true;
380                                 buf_length = 1;
381                                 FlushBuffer ();
382                                 return;
383                         }
384
385                         buf [buf_offset ++] = value;
386                         if (buf_offset > buf_length)
387                                 buf_length = buf_offset;
388
389                         buf_dirty = true;
390                 }
391
392                 public override int Read ([In,Out] byte[] dest, int dest_offset, int count)
393                 {
394                         if (handle == MonoIO.InvalidHandle)
395                                 throw new ObjectDisposedException ("Stream has been closed");
396                         if (dest == null)
397                                 throw new ArgumentNullException ("destFile");
398                         if (!CanRead)
399                                 throw new NotSupportedException ("Stream does not support reading");
400                         int len = dest.Length;
401                         if (dest_offset < 0)
402                                 throw new ArgumentOutOfRangeException ("dest_offset", "< 0");
403                         if (count < 0)
404                                 throw new ArgumentOutOfRangeException ("count", "< 0");
405                         if (dest_offset > len)
406                                 throw new ArgumentException ("destination offset is beyond array size");
407                         // reordered to avoid possible integer overflow
408                         if (dest_offset > len - count)
409                                 throw new ArgumentException ("Reading would overrun buffer");
410
411                         if (async) {
412                                 IAsyncResult ares = BeginRead (dest, dest_offset, count, null, null);
413                                 return EndRead (ares);
414                         }
415
416                         return ReadInternal (dest, dest_offset, count);
417                 }
418
419                 int ReadInternal (byte [] dest, int dest_offset, int count)
420                 {
421                         int copied = 0;
422
423                         int n = ReadSegment (dest, dest_offset, count);
424                         copied += n;
425                         count -= n;
426                         
427                         if (count == 0) {
428                                 /* If there was already enough
429                                  * buffered, no need to read
430                                  * more from the file.
431                                  */
432                                 return (copied);
433                         }
434
435                         if (count > buf_size) {
436                                 /* Read as much as we can, up
437                                  * to count bytes
438                                  */
439                                 FlushBuffer();
440                                 n = ReadData (handle, dest,
441                                               dest_offset+copied,
442                                               count);
443                         
444                                 /* Make the next buffer read
445                                  * start from the right place
446                                  */
447                                 buf_start += n;
448                         } else {
449                                 RefillBuffer ();
450                                 n = ReadSegment (dest,
451                                                  dest_offset+copied,
452                                                  count);
453                         }
454
455                         copied += n;
456
457                         return copied;
458                 }
459
460                 delegate int ReadDelegate (byte [] buffer, int offset, int count);
461
462                 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
463                                                         AsyncCallback cback, object state)
464                 {
465                         if (handle == MonoIO.InvalidHandle)
466                                 throw new ObjectDisposedException ("Stream has been closed");
467
468                         if (!CanRead)
469                                 throw new NotSupportedException ("This stream does not support reading");
470
471                         if (buffer == null)
472                                 throw new ArgumentNullException ("buffer");
473
474                         if (count < 0)
475                                 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
476
477                         if (offset < 0)
478                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
479
480                         // reordered to avoid possible integer overflow
481                         if (count > buffer.Length - offset)
482                                 throw new ArgumentException ("Buffer too small. count/offset wrong.");
483
484                         if (!async)
485                                 return base.BeginRead (buffer, offset, count, cback, state);
486
487                         ReadDelegate r = new ReadDelegate (ReadInternal);
488                         return r.BeginInvoke (buffer, offset, count, cback, state);                     
489                 }
490                 
491                 public override int EndRead (IAsyncResult async_result)
492                 {
493                         if (async_result == null)
494                                 throw new ArgumentNullException ("async_result");
495
496                         if (!async)
497                                 return base.EndRead (async_result);
498
499                         AsyncResult ares = async_result as AsyncResult;
500                         if (ares == null)
501                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
502
503                         ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
504                         if (r == null)
505                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
506
507                         return r.EndInvoke (async_result);
508                 }
509
510                 public override void Write (byte[] src, int src_offset, int count)
511                 {
512                         if (handle == MonoIO.InvalidHandle)
513                                 throw new ObjectDisposedException ("Stream has been closed");
514                         if (src == null)
515                                 throw new ArgumentNullException ("src");
516                         if (src_offset < 0)
517                                 throw new ArgumentOutOfRangeException ("src_offset", "< 0");
518                         if (count < 0)
519                                 throw new ArgumentOutOfRangeException ("count", "< 0");
520                         // ordered to avoid possible integer overflow
521                         if (src_offset > src.Length - count)
522                                 throw new ArgumentException ("Reading would overrun buffer");
523                         if (!CanWrite)
524                                 throw new NotSupportedException ("Stream does not support writing");
525
526                         if (async) {
527                                 IAsyncResult ares = BeginWrite (src, src_offset, count, null, null);
528                                 EndWrite (ares);
529                                 return;
530                         }
531
532                         WriteInternal (src, src_offset, count);
533                 }
534
535                 void WriteInternal (byte [] src, int src_offset, int count)
536                 {
537                         if (count > buf_size) {
538                                 // shortcut for long writes
539                                 MonoIOError error;
540
541                                 FlushBuffer ();
542
543                                 MonoIO.Write (handle, src, src_offset, count, out error);
544                                 if (error != MonoIOError.ERROR_SUCCESS) {
545                                         // don't leak the path information for isolated storage
546                                         string fname = (anonymous) ? Path.GetFileName (name) : name;
547                                         throw MonoIO.GetException (fname, error);
548                                 }
549                                 
550                                 buf_start += count;
551                         } else {
552
553                                 int copied = 0;
554                                 while (count > 0) {
555                                         
556                                         int n = WriteSegment (src, src_offset + copied, count);
557                                         copied += n;
558                                         count -= n;
559
560                                         if (count == 0) {
561                                                 break;
562                                         }
563
564                                         FlushBuffer ();
565                                 }
566                         }
567                 }
568
569                 delegate void WriteDelegate (byte [] buffer, int offset, int count);
570
571                 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
572                                                         AsyncCallback cback, object state)
573                 {
574                         if (handle == MonoIO.InvalidHandle)
575                                 throw new ObjectDisposedException ("Stream has been closed");
576
577                         if (!CanWrite)
578                                 throw new NotSupportedException ("This stream does not support writing");
579
580                         if (buffer == null)
581                                 throw new ArgumentNullException ("buffer");
582
583                         if (count < 0)
584                                 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
585
586                         if (offset < 0)
587                                 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
588
589                         // reordered to avoid possible integer overflow
590                         if (count > buffer.Length - offset)
591                                 throw new ArgumentException ("Buffer too small. count/offset wrong.");
592
593                         if (!async)
594                                 return base.BeginWrite (buffer, offset, count, cback, state);
595
596                         FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
597                         result.BytesRead = -1;
598                         result.Count = count;
599                         result.OriginalCount = count;
600
601                         if (buf_dirty) {
602                                 MemoryStream ms = new MemoryStream ();
603                                 FlushBufferToStream (ms);
604                                 ms.Write (buffer, offset, count);
605                                 offset = 0;
606                                 count = (int) ms.Length;
607                         }
608
609                         WriteDelegate w = new WriteDelegate (WriteInternal);
610                         return w.BeginInvoke (buffer, offset, count, cback, state);                     
611                 }
612                 
613                 public override void EndWrite (IAsyncResult async_result)
614                 {
615                         if (async_result == null)
616                                 throw new ArgumentNullException ("async_result");
617
618                         if (!async) {
619                                 base.EndWrite (async_result);
620                                 return;
621                         }
622
623                         AsyncResult ares = async_result as AsyncResult;
624                         if (ares == null)
625                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
626
627                         WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
628                         if (w == null)
629                                 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
630
631                         w.EndInvoke (async_result);
632                         return;
633                 }
634
635                 public override long Seek (long offset, SeekOrigin origin)
636                 {
637                         long pos;
638
639                         if (handle == MonoIO.InvalidHandle)
640                                 throw new ObjectDisposedException ("Stream has been closed");
641                         
642                         // make absolute
643
644                         if(CanSeek == false) {
645                                 throw new NotSupportedException("The stream does not support seeking");
646                         }
647
648                         switch (origin) {
649                         case SeekOrigin.End:
650                                 pos = Length + offset;
651                                 break;
652
653                         case SeekOrigin.Current:
654                                 pos = Position + offset;
655                                 break;
656
657                         case SeekOrigin.Begin:
658                                 pos = offset;
659                                 break;
660
661                         default:
662                                 throw new ArgumentException ("origin", "Invalid SeekOrigin");
663                         }
664
665                         if (pos < 0) {
666                                 /* LAMESPEC: shouldn't this be
667                                  * ArgumentOutOfRangeException?
668                                  */
669                                 throw new IOException("Attempted to Seek before the beginning of the stream");
670                         }
671
672                         if(pos < this.append_startpos) {
673                                 /* More undocumented crap */
674                                 throw new IOException("Can't seek back over pre-existing data in append mode");
675                         }
676
677                         if (buf_length > 0) {
678                                 if (pos >= buf_start &&
679                                         pos <= buf_start + buf_length) {
680                                         buf_offset = (int) (pos - buf_start);
681                                         return pos;
682                                 }
683                         }
684
685                         FlushBuffer ();
686
687                         MonoIOError error;
688                 
689                         buf_start = MonoIO.Seek (handle, pos,
690                                                  SeekOrigin.Begin,
691                                                  out error);
692
693                         if (error != MonoIOError.ERROR_SUCCESS) {
694                                 // don't leak the path information for isolated storage
695                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
696                                 throw MonoIO.GetException (fname, error);
697                         }
698                         
699                         return(buf_start);
700                 }
701
702                 public override void SetLength (long length)
703                 {
704                         if (handle == MonoIO.InvalidHandle)
705                                 throw new ObjectDisposedException ("Stream has been closed");
706
707                         if(CanSeek == false)
708                                 throw new NotSupportedException("The stream does not support seeking");
709
710                         if(CanWrite == false)
711                                 throw new NotSupportedException("The stream does not support writing");
712
713                         if(length < 0)
714                                 throw new ArgumentOutOfRangeException("Length is less than 0");
715                         
716                         Flush ();
717
718                         MonoIOError error;
719                         
720                         MonoIO.SetLength (handle, length, out error);
721                         if (error != MonoIOError.ERROR_SUCCESS) {
722                                 // don't leak the path information for isolated storage
723                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
724                                 throw MonoIO.GetException (fname, error);
725                         }
726
727                         if (Position > length)
728                                 Position = length;
729                 }
730
731                 public override void Flush ()
732                 {
733                         if (handle == MonoIO.InvalidHandle)
734                                 throw new ObjectDisposedException ("Stream has been closed");
735
736                         FlushBuffer ();
737                         
738                         // The flushing is not actually required, in
739                         //the mono runtime we were mapping flush to
740                         //`fsync' which is not the same.
741                         //
742                         //MonoIO.Flush (handle);
743                 }
744
745                 public override void Close ()
746                 {
747                         Dispose (true);
748                         GC.SuppressFinalize (this);     // remove from finalize queue
749                 }
750
751                 public virtual void Lock (long position, long length)
752                 {
753                         if (handle == MonoIO.InvalidHandle)
754                                 throw new ObjectDisposedException ("Stream has been closed");
755                         if (position < 0) {
756                                 throw new ArgumentOutOfRangeException ("position must not be negative");
757                         }
758                         if (length < 0) {
759                                 throw new ArgumentOutOfRangeException ("length must not be negative");
760                         }
761                         if (handle == MonoIO.InvalidHandle) {
762                                 throw new ObjectDisposedException ("Stream has been closed");
763                         }
764                                 
765                         MonoIOError error;
766
767                         MonoIO.Lock (handle, position, length, out error);
768                         if (error != MonoIOError.ERROR_SUCCESS) {
769                                 // don't leak the path information for isolated storage
770                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
771                                 throw MonoIO.GetException (fname, error);
772                         }
773                 }
774
775                 public virtual void Unlock (long position, long length)
776                 {
777                         if (handle == MonoIO.InvalidHandle)
778                                 throw new ObjectDisposedException ("Stream has been closed");
779                         if (position < 0) {
780                                 throw new ArgumentOutOfRangeException ("position must not be negative");
781                         }
782                         if (length < 0) {
783                                 throw new ArgumentOutOfRangeException ("length must not be negative");
784                         }
785                                 
786                         MonoIOError error;
787
788                         MonoIO.Unlock (handle, position, length, out error);
789                         if (error != MonoIOError.ERROR_SUCCESS) {
790                                 // don't leak the path information for isolated storage
791                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
792                                 throw MonoIO.GetException (fname, error);
793                         }
794                 }
795
796                 // protected
797
798                 ~FileStream ()
799                 {
800                         Dispose (false);
801                 }
802
803                 protected virtual void Dispose (bool disposing) {
804                         if (handle != MonoIO.InvalidHandle) {
805                                 FlushBuffer ();
806
807                                 if (owner) {
808                                         MonoIOError error;
809                                 
810                                         MonoIO.Close (handle, out error);
811                                         if (error != MonoIOError.ERROR_SUCCESS) {
812                                                 // don't leak the path information for isolated storage
813                                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
814                                                 throw MonoIO.GetException (fname, error);
815                                         }
816
817                                         handle = MonoIO.InvalidHandle;
818                                 }
819                         }
820
821                         canseek = false;
822                         access = 0;
823                         if (disposing) {
824                                 buf = null;
825                         }
826                 }
827
828                 // private.
829
830                 // ReadSegment, WriteSegment, FlushBuffer,
831                 // RefillBuffer and ReadData should only be called
832                 // when the Monitor lock is held, but these methods
833                 // grab it again just to be safe.
834
835                 private int ReadSegment (byte [] dest, int dest_offset, int count)
836                 {
837                         if (count > buf_length - buf_offset) {
838                                 count = buf_length - buf_offset;
839                         }
840                         
841                         if (count > 0) {
842                                 Buffer.BlockCopy (buf, buf_offset,
843                                                   dest, dest_offset,
844                                                   count);
845                                 buf_offset += count;
846                         }
847                         
848                         return(count);
849                 }
850
851                 private int WriteSegment (byte [] src, int src_offset,
852                                           int count)
853                 {
854                         if (count > buf_size - buf_offset) {
855                                 count = buf_size - buf_offset;
856                         }
857                         
858                         if (count > 0) {
859                                 Buffer.BlockCopy (src, src_offset,
860                                                   buf, buf_offset,
861                                                   count);
862                                 buf_offset += count;
863                                 if (buf_offset > buf_length) {
864                                         buf_length = buf_offset;
865                                 }
866                                 
867                                 buf_dirty = true;
868                         }
869                         
870                         return(count);
871                 }
872
873                 void FlushBufferToStream (Stream st)
874                 {
875                         if (buf_dirty) {
876                                 if (CanSeek == true) {
877                                         MonoIOError error;
878                                         MonoIO.Seek (handle, buf_start,
879                                                      SeekOrigin.Begin,
880                                                      out error);
881                                         if (error != MonoIOError.ERROR_SUCCESS) {
882                                                 // don't leak the path information for isolated storage
883                                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
884                                                 throw MonoIO.GetException (fname, error);
885                                         }
886                                 }
887                                 st.Write (buf, 0, buf_length);
888                         }
889
890                         buf_start += buf_offset;
891                         buf_offset = buf_length = 0;
892                         buf_dirty = false;
893                 }
894
895                 private void FlushBuffer ()
896                 {
897                         if (buf_dirty) {
898                                 MonoIOError error;
899                                 
900                                 if (CanSeek == true) {
901                                         MonoIO.Seek (handle, buf_start,
902                                                      SeekOrigin.Begin,
903                                                      out error);
904                                         if (error != MonoIOError.ERROR_SUCCESS) {
905                                                 // don't leak the path information for isolated storage
906                                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
907                                                 throw MonoIO.GetException (fname, error);
908                                         }
909                                 }
910                                 MonoIO.Write (handle, buf, 0,
911                                               buf_length, out error);
912
913                                 if (error != MonoIOError.ERROR_SUCCESS) {
914                                         // don't leak the path information for isolated storage
915                                         string fname = (anonymous) ? Path.GetFileName (name) : name;
916                                         throw MonoIO.GetException (fname, error);
917                                 }
918                         }
919
920                         buf_start += buf_offset;
921                         buf_offset = buf_length = 0;
922                         buf_dirty = false;
923                 }
924
925                 private void FlushBufferIfDirty ()
926                 {
927                         if (buf_dirty)
928                                 FlushBuffer ();
929                 }
930
931                 private void RefillBuffer ()
932                 {
933                         FlushBuffer();
934                         
935                         buf_length = ReadData (handle, buf, 0,
936                                                buf_size);
937                 }
938
939                 private int ReadData (IntPtr handle, byte[] buf, int offset,
940                                       int count)
941                 {
942                         MonoIOError error;
943                         int amount = 0;
944
945                         /* when async == true, if we get here we don't suport AIO or it's disabled
946                          * and we're using the threadpool */
947                         amount = MonoIO.Read (handle, buf, offset, count, out error);
948                         if (error == MonoIOError.ERROR_BROKEN_PIPE) {
949                                 amount = 0; // might not be needed, but well...
950                         } else if (error != MonoIOError.ERROR_SUCCESS) {
951                                 // don't leak the path information for isolated storage
952                                 string fname = (anonymous) ? Path.GetFileName (name) : name;
953                                 throw MonoIO.GetException (fname, error);
954                         }
955                         
956                         /* Check for read error */
957                         if(amount == -1) {
958                                 throw new IOException ();
959                         }
960                         
961                         return(amount);
962                 }
963                                 
964                 private void InitBuffer (int size, bool noBuffering)
965                 {
966                         if (noBuffering) {
967                                 size = 0;
968                                 // We need a buffer for the ReadByte method. This buffer won't
969                                 // be used for anything else since buf_size==0.
970                                 buf = new byte [1];
971                         }
972                         else {
973                                 if (size <= 0)
974                                         throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
975                                 if (size < 8)
976                                         size = 8;
977                                 buf = new byte [size];
978                         }
979                                         
980                         buf_size = size;
981                         buf_start = 0;
982                         buf_offset = buf_length = 0;
983                         buf_dirty = false;
984                 }
985
986                 // fields
987
988                 internal const int DefaultBufferSize = 8192;
989
990                 private FileAccess access;
991                 private bool owner;
992                 private bool async;
993                 private bool canseek;
994                 private long append_startpos;
995                 private bool anonymous;
996
997                 private byte [] buf;                    // the buffer
998                 private int buf_size;                   // capacity in bytes
999                 private int buf_length;                 // number of valid bytes in buffer
1000                 private int buf_offset;                 // position of next byte
1001                 private bool buf_dirty;                 // true if buffer has been written to
1002                 private long buf_start;                 // location of buffer in file
1003                 private string name = "[Unknown]";      // name of file.
1004
1005                 IntPtr handle;                          // handle to underlying file
1006         }
1007 }
1008