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