2007-05-12 Jonathan Chambers <joncham@gmail.com>
[mcs.git] / class / corlib / System.IO / FileStream.cs
blobf7d7423371ca437b1d5daa54753f3409bd70b912
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)
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.
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;
40 #if NET_2_0
41 using Microsoft.Win32.SafeHandles;
42 using System.Security.AccessControl;
43 #endif
45 namespace System.IO
47 #if NET_2_0
48 [ComVisible (true)]
49 #endif
50 public class FileStream : Stream
52 // construct from handle
54 #if NET_2_0_SAFEFILEHANDLE_ENABLED
55 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
56 #endif
57 public FileStream (IntPtr handle, FileAccess access)
58 : this (handle, access, true, DefaultBufferSize, false) {}
60 #if NET_2_0_SAFEFILEHANDLE_ENABLED
61 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
62 #endif
63 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
64 : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
66 #if NET_2_0_SAFEFILEHANDLE_ENABLED
67 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
68 #endif
69 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
70 : this (handle, access, ownsHandle, bufferSize, false) {}
72 #if NET_2_0_SAFEFILEHANDLE_ENABLED
73 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
74 #endif
75 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
76 : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
78 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
79 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool noBuffering)
81 this.handle = MonoIO.InvalidHandle;
82 if (handle == this.handle)
83 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
85 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
86 throw new ArgumentOutOfRangeException ("access");
88 MonoIOError error;
89 MonoFileType ftype = MonoIO.GetFileType (handle, out error);
91 if (error != MonoIOError.ERROR_SUCCESS) {
92 throw MonoIO.GetException (name, error);
95 if (ftype == MonoFileType.Unknown) {
96 throw new IOException ("Invalid handle.");
97 } else if (ftype == MonoFileType.Disk) {
98 this.canseek = true;
99 } else {
100 this.canseek = false;
103 this.handle = handle;
104 this.access = access;
105 this.owner = ownsHandle;
106 this.async = isAsync;
107 this.anonymous = false;
109 InitBuffer (bufferSize, noBuffering);
111 if (canseek) {
112 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
113 if (error != MonoIOError.ERROR_SUCCESS) {
114 throw MonoIO.GetException (name, error);
118 /* Can't set append mode */
119 this.append_startpos=0;
122 // construct from filename
124 public FileStream (string name, FileMode mode)
125 : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
129 public FileStream (string name, FileMode mode, FileAccess access)
130 : this (name, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
134 public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
135 : this (name, mode, access, share, DefaultBufferSize, false, FileOptions.None)
139 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
140 : this (name, mode, access, share, bufferSize, false, FileOptions.None)
144 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
145 : this (name, mode, access, share, bufferSize, isAsync, FileOptions.None)
149 #if NET_2_0
150 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
151 : this (name, mode, access, share, bufferSize, false, options)
155 public FileStream (SafeFileHandle handle, FileAccess access)
156 :this(handle, access, DefaultBufferSize, false)
160 public FileStream (SafeFileHandle handle, FileAccess access,
161 int bufferSize)
162 :this(handle, access, bufferSize, false)
166 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
167 public FileStream (SafeFileHandle handle, FileAccess access,
168 int bufferSize, bool isAsync)
169 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
173 public FileStream (string path, FileMode mode,
174 FileSystemRights rights, FileShare share,
175 int bufferSize, FileOptions options)
177 throw new NotImplementedException ();
180 public FileStream (string path, FileMode mode,
181 FileSystemRights rights, FileShare share,
182 int bufferSize, FileOptions options,
183 FileSecurity fileSecurity)
185 throw new NotImplementedException ();
187 #endif
189 internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
190 : this (name, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
194 internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
196 if (name == null) {
197 throw new ArgumentNullException ("name");
200 if (name.Length == 0) {
201 throw new ArgumentException ("Name is empty");
204 #if NET_2_0
205 // ignore the Inheritable flag
206 share &= ~FileShare.Inheritable;
208 #endif
210 if (bufferSize <= 0)
211 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
213 if (mode < FileMode.CreateNew || mode > FileMode.Append)
214 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
216 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
217 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
219 #if NET_2_0
220 if (share < FileShare.None || share > FileShare.Delete)
221 #else
222 if (share < FileShare.None || share > FileShare.ReadWrite)
223 #endif
224 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
226 if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
227 throw new ArgumentException ("Name has invalid chars");
230 if (Directory.Exists (name)) {
231 // don't leak the path information for isolated storage
232 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
233 string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
234 throw new UnauthorizedAccessException (String.Format (msg, fname));
237 /* Append streams can't be read (see FileMode
238 * docs)
240 if (mode==FileMode.Append &&
241 (access&FileAccess.Read)==FileAccess.Read) {
242 throw new ArgumentException("Append access can be requested only in write-only mode.");
245 if ((access & FileAccess.Write) == 0 &&
246 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
247 string msg = Locale.GetText ("Combining FileMode: {0} with " +
248 "FileAccess: {1} is invalid.");
249 throw new ArgumentException (string.Format (msg, access, mode));
252 string dname = Path.GetDirectoryName (name);
253 if (dname.Length > 0) {
254 string fp = Path.GetFullPath (dname);
255 if (!Directory.Exists (fp)) {
256 // don't leak the path information for isolated storage
257 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
258 string fname = (anonymous) ? dname : Path.GetFullPath (name);
259 throw new DirectoryNotFoundException (String.Format (msg, fname));
263 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
264 mode != FileMode.CreateNew && !File.Exists (name)) {
265 // don't leak the path information for isolated storage
266 string msg = Locale.GetText ("Could not find file \"{0}\".");
267 string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
268 throw new FileNotFoundException (String.Format (msg, fname), fname);
271 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
272 if (!anonymous)
273 this.name = name;
275 // TODO: demand permissions
277 MonoIOError error;
279 this.handle = MonoIO.Open (name, mode, access, share, options, out error);
280 if (handle == MonoIO.InvalidHandle) {
281 // don't leak the path information for isolated storage
282 string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
283 throw MonoIO.GetException (fname, error);
286 this.access = access;
287 this.owner = true;
288 this.anonymous = anonymous;
290 /* Can we open non-files by name? */
292 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
293 this.canseek = true;
294 this.async = (options & FileOptions.Asynchronous) != 0;
295 } else {
296 this.canseek = false;
297 this.async = false;
301 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
302 /* Avoid allocating a large buffer for small files */
303 long len = Length;
304 if (bufferSize > len) {
305 bufferSize = (int)(len < 1000 ? 1000 : len);
309 InitBuffer (bufferSize, false);
311 if (mode==FileMode.Append) {
312 this.Seek (0, SeekOrigin.End);
313 this.append_startpos=this.Position;
314 } else {
315 this.append_startpos=0;
319 // properties
321 public override bool CanRead {
322 get {
323 return access == FileAccess.Read ||
324 access == FileAccess.ReadWrite;
328 public override bool CanWrite {
329 get {
330 return access == FileAccess.Write ||
331 access == FileAccess.ReadWrite;
335 public override bool CanSeek {
336 get {
337 return(canseek);
341 public virtual bool IsAsync {
342 get {
343 return (async);
347 public string Name {
348 get {
349 return name;
353 public override long Length {
354 get {
355 if (handle == MonoIO.InvalidHandle)
356 throw new ObjectDisposedException ("Stream has been closed");
358 if (!CanSeek)
359 throw new NotSupportedException ("The stream does not support seeking");
361 // Buffered data might change the length of the stream
362 FlushBufferIfDirty ();
364 MonoIOError error;
365 long length;
367 length = MonoIO.GetLength (handle, out error);
368 if (error != MonoIOError.ERROR_SUCCESS) {
369 // don't leak the path information for isolated storage
370 string fname = (anonymous) ? Path.GetFileName (name) : name;
371 throw MonoIO.GetException (fname, error);
374 return(length);
378 public override long Position {
379 get {
380 if (handle == MonoIO.InvalidHandle)
381 throw new ObjectDisposedException ("Stream has been closed");
383 if(CanSeek == false)
384 throw new NotSupportedException("The stream does not support seeking");
386 return(buf_start + buf_offset);
388 set {
389 if (handle == MonoIO.InvalidHandle)
390 throw new ObjectDisposedException ("Stream has been closed");
392 if(CanSeek == false) {
393 throw new NotSupportedException("The stream does not support seeking");
396 if(value < 0) {
397 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
400 Seek (value, SeekOrigin.Begin);
404 #if NET_2_0_SAFEFILEHANDLE_ENABLED
405 [Obsolete ("Use SafeFileHandle instead")]
406 #endif
407 public virtual IntPtr Handle {
408 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
409 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
410 get {
411 return handle;
415 #if NET_2_0
416 public virtual SafeFileHandle SafeFileHandle {
417 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
418 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
419 get { throw new NotImplementedException (); }
421 #endif
423 // methods
425 public override int ReadByte ()
427 if (handle == MonoIO.InvalidHandle)
428 throw new ObjectDisposedException ("Stream has been closed");
430 if (!CanRead)
431 throw new NotSupportedException ("Stream does not support reading");
433 if (buf_size == 0) {
434 int n = ReadData (handle, buf, 0, 1);
435 if (n == 0) return -1;
436 else return buf[0];
438 else if (buf_offset >= buf_length) {
439 RefillBuffer ();
441 if (buf_length == 0)
442 return -1;
445 return buf [buf_offset ++];
448 public override void WriteByte (byte value)
450 if (handle == MonoIO.InvalidHandle)
451 throw new ObjectDisposedException ("Stream has been closed");
453 if (!CanWrite)
454 throw new NotSupportedException ("Stream does not support writing");
456 if (buf_offset == buf_size)
457 FlushBuffer ();
459 if (buf_size == 0) { // No buffering
460 buf [0] = value;
461 buf_dirty = true;
462 buf_length = 1;
463 FlushBuffer ();
464 return;
467 buf [buf_offset ++] = value;
468 if (buf_offset > buf_length)
469 buf_length = buf_offset;
471 buf_dirty = true;
474 public override int Read ([In,Out] byte[] dest, int dest_offset, int count)
476 if (handle == MonoIO.InvalidHandle)
477 throw new ObjectDisposedException ("Stream has been closed");
478 if (dest == null)
479 throw new ArgumentNullException ("destFile");
480 if (!CanRead)
481 throw new NotSupportedException ("Stream does not support reading");
482 int len = dest.Length;
483 if (dest_offset < 0)
484 throw new ArgumentOutOfRangeException ("dest_offset", "< 0");
485 if (count < 0)
486 throw new ArgumentOutOfRangeException ("count", "< 0");
487 if (dest_offset > len)
488 throw new ArgumentException ("destination offset is beyond array size");
489 // reordered to avoid possible integer overflow
490 if (dest_offset > len - count)
491 throw new ArgumentException ("Reading would overrun buffer");
493 if (async) {
494 IAsyncResult ares = BeginRead (dest, dest_offset, count, null, null);
495 return EndRead (ares);
498 return ReadInternal (dest, dest_offset, count);
501 int ReadInternal (byte [] dest, int dest_offset, int count)
503 int copied = 0;
505 int n = ReadSegment (dest, dest_offset, count);
506 copied += n;
507 count -= n;
509 if (count == 0) {
510 /* If there was already enough
511 * buffered, no need to read
512 * more from the file.
514 return (copied);
517 if (count > buf_size) {
518 /* Read as much as we can, up
519 * to count bytes
521 FlushBuffer();
522 n = ReadData (handle, dest,
523 dest_offset+copied,
524 count);
526 /* Make the next buffer read
527 * start from the right place
529 buf_start += n;
530 } else {
531 RefillBuffer ();
532 n = ReadSegment (dest,
533 dest_offset+copied,
534 count);
537 copied += n;
539 return copied;
542 delegate int ReadDelegate (byte [] buffer, int offset, int count);
544 public override IAsyncResult BeginRead (byte [] buffer, int offset, int count,
545 AsyncCallback cback, object state)
547 if (handle == MonoIO.InvalidHandle)
548 throw new ObjectDisposedException ("Stream has been closed");
550 if (!CanRead)
551 throw new NotSupportedException ("This stream does not support reading");
553 if (buffer == null)
554 throw new ArgumentNullException ("buffer");
556 if (count < 0)
557 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
559 if (offset < 0)
560 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
562 // reordered to avoid possible integer overflow
563 if (count > buffer.Length - offset)
564 throw new ArgumentException ("Buffer too small. count/offset wrong.");
566 if (!async)
567 return base.BeginRead (buffer, offset, count, cback, state);
569 ReadDelegate r = new ReadDelegate (ReadInternal);
570 return r.BeginInvoke (buffer, offset, count, cback, state);
573 public override int EndRead (IAsyncResult async_result)
575 if (async_result == null)
576 throw new ArgumentNullException ("async_result");
578 if (!async)
579 return base.EndRead (async_result);
581 AsyncResult ares = async_result as AsyncResult;
582 if (ares == null)
583 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
585 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
586 if (r == null)
587 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
589 return r.EndInvoke (async_result);
592 public override void Write (byte[] src, int src_offset, int count)
594 if (handle == MonoIO.InvalidHandle)
595 throw new ObjectDisposedException ("Stream has been closed");
596 if (src == null)
597 throw new ArgumentNullException ("src");
598 if (src_offset < 0)
599 throw new ArgumentOutOfRangeException ("src_offset", "< 0");
600 if (count < 0)
601 throw new ArgumentOutOfRangeException ("count", "< 0");
602 // ordered to avoid possible integer overflow
603 if (src_offset > src.Length - count)
604 throw new ArgumentException ("Reading would overrun buffer");
605 if (!CanWrite)
606 throw new NotSupportedException ("Stream does not support writing");
608 if (async) {
609 IAsyncResult ares = BeginWrite (src, src_offset, count, null, null);
610 EndWrite (ares);
611 return;
614 WriteInternal (src, src_offset, count);
617 void WriteInternal (byte [] src, int src_offset, int count)
619 if (count > buf_size) {
620 // shortcut for long writes
621 MonoIOError error;
623 FlushBuffer ();
625 MonoIO.Write (handle, src, src_offset, count, out error);
626 if (error != MonoIOError.ERROR_SUCCESS) {
627 // don't leak the path information for isolated storage
628 string fname = (anonymous) ? Path.GetFileName (name) : name;
629 throw MonoIO.GetException (fname, error);
632 buf_start += count;
633 } else {
635 int copied = 0;
636 while (count > 0) {
638 int n = WriteSegment (src, src_offset + copied, count);
639 copied += n;
640 count -= n;
642 if (count == 0) {
643 break;
646 FlushBuffer ();
651 delegate void WriteDelegate (byte [] buffer, int offset, int count);
653 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count,
654 AsyncCallback cback, object state)
656 if (handle == MonoIO.InvalidHandle)
657 throw new ObjectDisposedException ("Stream has been closed");
659 if (!CanWrite)
660 throw new NotSupportedException ("This stream does not support writing");
662 if (buffer == null)
663 throw new ArgumentNullException ("buffer");
665 if (count < 0)
666 throw new ArgumentOutOfRangeException ("count", "Must be >= 0");
668 if (offset < 0)
669 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
671 // reordered to avoid possible integer overflow
672 if (count > buffer.Length - offset)
673 throw new ArgumentException ("Buffer too small. count/offset wrong.");
675 if (!async)
676 return base.BeginWrite (buffer, offset, count, cback, state);
678 FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
679 result.BytesRead = -1;
680 result.Count = count;
681 result.OriginalCount = count;
683 if (buf_dirty) {
684 MemoryStream ms = new MemoryStream ();
685 FlushBufferToStream (ms);
686 ms.Write (buffer, offset, count);
687 offset = 0;
688 count = (int) ms.Length;
691 WriteDelegate w = new WriteDelegate (WriteInternal);
692 return w.BeginInvoke (buffer, offset, count, cback, state);
695 public override void EndWrite (IAsyncResult async_result)
697 if (async_result == null)
698 throw new ArgumentNullException ("async_result");
700 if (!async) {
701 base.EndWrite (async_result);
702 return;
705 AsyncResult ares = async_result as AsyncResult;
706 if (ares == null)
707 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
709 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
710 if (w == null)
711 throw new ArgumentException ("Invalid IAsyncResult", "async_result");
713 w.EndInvoke (async_result);
714 return;
717 public override long Seek (long offset, SeekOrigin origin)
719 long pos;
721 if (handle == MonoIO.InvalidHandle)
722 throw new ObjectDisposedException ("Stream has been closed");
724 // make absolute
726 if(CanSeek == false) {
727 throw new NotSupportedException("The stream does not support seeking");
730 switch (origin) {
731 case SeekOrigin.End:
732 pos = Length + offset;
733 break;
735 case SeekOrigin.Current:
736 pos = Position + offset;
737 break;
739 case SeekOrigin.Begin:
740 pos = offset;
741 break;
743 default:
744 throw new ArgumentException ("origin", "Invalid SeekOrigin");
747 if (pos < 0) {
748 /* LAMESPEC: shouldn't this be
749 * ArgumentOutOfRangeException?
751 throw new IOException("Attempted to Seek before the beginning of the stream");
754 if(pos < this.append_startpos) {
755 /* More undocumented crap */
756 throw new IOException("Can't seek back over pre-existing data in append mode");
759 FlushBuffer ();
761 MonoIOError error;
763 buf_start = MonoIO.Seek (handle, pos,
764 SeekOrigin.Begin,
765 out error);
767 if (error != MonoIOError.ERROR_SUCCESS) {
768 // don't leak the path information for isolated storage
769 string fname = (anonymous) ? Path.GetFileName (name) : name;
770 throw MonoIO.GetException (fname, error);
773 return(buf_start);
776 public override void SetLength (long length)
778 if (handle == MonoIO.InvalidHandle)
779 throw new ObjectDisposedException ("Stream has been closed");
781 if(CanSeek == false)
782 throw new NotSupportedException("The stream does not support seeking");
784 if(CanWrite == false)
785 throw new NotSupportedException("The stream does not support writing");
787 if(length < 0)
788 throw new ArgumentOutOfRangeException("Length is less than 0");
790 Flush ();
792 MonoIOError error;
794 MonoIO.SetLength (handle, length, out error);
795 if (error != MonoIOError.ERROR_SUCCESS) {
796 // don't leak the path information for isolated storage
797 string fname = (anonymous) ? Path.GetFileName (name) : name;
798 throw MonoIO.GetException (fname, error);
801 if (Position > length)
802 Position = length;
805 public override void Flush ()
807 if (handle == MonoIO.InvalidHandle)
808 throw new ObjectDisposedException ("Stream has been closed");
810 FlushBuffer ();
812 // The flushing is not actually required, in
813 //the mono runtime we were mapping flush to
814 //`fsync' which is not the same.
816 //MonoIO.Flush (handle);
819 public override void Close ()
821 Dispose (true);
822 GC.SuppressFinalize (this); // remove from finalize queue
825 public virtual void Lock (long position, long length)
827 if (handle == MonoIO.InvalidHandle)
828 throw new ObjectDisposedException ("Stream has been closed");
829 if (position < 0) {
830 throw new ArgumentOutOfRangeException ("position must not be negative");
832 if (length < 0) {
833 throw new ArgumentOutOfRangeException ("length must not be negative");
835 if (handle == MonoIO.InvalidHandle) {
836 throw new ObjectDisposedException ("Stream has been closed");
839 MonoIOError error;
841 MonoIO.Lock (handle, position, length, out error);
842 if (error != MonoIOError.ERROR_SUCCESS) {
843 // don't leak the path information for isolated storage
844 string fname = (anonymous) ? Path.GetFileName (name) : name;
845 throw MonoIO.GetException (fname, error);
849 public virtual void Unlock (long position, long length)
851 if (handle == MonoIO.InvalidHandle)
852 throw new ObjectDisposedException ("Stream has been closed");
853 if (position < 0) {
854 throw new ArgumentOutOfRangeException ("position must not be negative");
856 if (length < 0) {
857 throw new ArgumentOutOfRangeException ("length must not be negative");
860 MonoIOError error;
862 MonoIO.Unlock (handle, position, length, out error);
863 if (error != MonoIOError.ERROR_SUCCESS) {
864 // don't leak the path information for isolated storage
865 string fname = (anonymous) ? Path.GetFileName (name) : name;
866 throw MonoIO.GetException (fname, error);
870 // protected
872 ~FileStream ()
874 Dispose (false);
877 #if NET_2_0
878 protected override void Dispose (bool disposing)
879 #else
880 protected virtual void Dispose (bool disposing)
881 #endif
883 if (handle != MonoIO.InvalidHandle) {
884 FlushBuffer ();
886 if (owner) {
887 MonoIOError error;
889 MonoIO.Close (handle, out error);
890 if (error != MonoIOError.ERROR_SUCCESS) {
891 // don't leak the path information for isolated storage
892 string fname = (anonymous) ? Path.GetFileName (name) : name;
893 throw MonoIO.GetException (fname, error);
896 handle = MonoIO.InvalidHandle;
900 canseek = false;
901 access = 0;
902 if (disposing) {
903 buf = null;
907 #if NET_2_0
908 public FileSecurity GetAccessControl ()
910 throw new NotImplementedException ();
913 public void SetAccessControl (FileSecurity fileSecurity)
915 throw new NotImplementedException ();
917 #endif
919 // private.
921 // ReadSegment, WriteSegment, FlushBuffer,
922 // RefillBuffer and ReadData should only be called
923 // when the Monitor lock is held, but these methods
924 // grab it again just to be safe.
926 private int ReadSegment (byte [] dest, int dest_offset, int count)
928 if (count > buf_length - buf_offset) {
929 count = buf_length - buf_offset;
932 if (count > 0) {
933 Buffer.BlockCopy (buf, buf_offset,
934 dest, dest_offset,
935 count);
936 buf_offset += count;
939 return(count);
942 private int WriteSegment (byte [] src, int src_offset,
943 int count)
945 if (count > buf_size - buf_offset) {
946 count = buf_size - buf_offset;
949 if (count > 0) {
950 Buffer.BlockCopy (src, src_offset,
951 buf, buf_offset,
952 count);
953 buf_offset += count;
954 if (buf_offset > buf_length) {
955 buf_length = buf_offset;
958 buf_dirty = true;
961 return(count);
964 void FlushBufferToStream (Stream st)
966 if (buf_dirty) {
967 if (CanSeek == true) {
968 MonoIOError error;
969 MonoIO.Seek (handle, buf_start,
970 SeekOrigin.Begin,
971 out error);
972 if (error != MonoIOError.ERROR_SUCCESS) {
973 // don't leak the path information for isolated storage
974 string fname = (anonymous) ? Path.GetFileName (name) : name;
975 throw MonoIO.GetException (fname, error);
978 st.Write (buf, 0, buf_length);
981 buf_start += buf_offset;
982 buf_offset = buf_length = 0;
983 buf_dirty = false;
986 private void FlushBuffer ()
988 if (buf_dirty) {
989 MonoIOError error;
991 if (CanSeek == true) {
992 MonoIO.Seek (handle, buf_start,
993 SeekOrigin.Begin,
994 out error);
995 if (error != MonoIOError.ERROR_SUCCESS) {
996 // don't leak the path information for isolated storage
997 string fname = (anonymous) ? Path.GetFileName (name) : name;
998 throw MonoIO.GetException (fname, error);
1001 MonoIO.Write (handle, buf, 0,
1002 buf_length, out error);
1004 if (error != MonoIOError.ERROR_SUCCESS) {
1005 // don't leak the path information for isolated storage
1006 string fname = (anonymous) ? Path.GetFileName (name) : name;
1007 throw MonoIO.GetException (fname, error);
1011 buf_start += buf_offset;
1012 buf_offset = buf_length = 0;
1013 buf_dirty = false;
1016 private void FlushBufferIfDirty ()
1018 if (buf_dirty)
1019 FlushBuffer ();
1022 private void RefillBuffer ()
1024 FlushBuffer();
1026 buf_length = ReadData (handle, buf, 0,
1027 buf_size);
1030 private int ReadData (IntPtr handle, byte[] buf, int offset,
1031 int count)
1033 MonoIOError error;
1034 int amount = 0;
1036 /* when async == true, if we get here we don't suport AIO or it's disabled
1037 * and we're using the threadpool */
1038 amount = MonoIO.Read (handle, buf, offset, count, out error);
1039 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1040 amount = 0; // might not be needed, but well...
1041 } else if (error != MonoIOError.ERROR_SUCCESS) {
1042 // don't leak the path information for isolated storage
1043 string fname = (anonymous) ? Path.GetFileName (name) : name;
1044 throw MonoIO.GetException (fname, error);
1047 /* Check for read error */
1048 if(amount == -1) {
1049 throw new IOException ();
1052 return(amount);
1055 private void InitBuffer (int size, bool noBuffering)
1057 if (noBuffering) {
1058 size = 0;
1059 // We need a buffer for the ReadByte method. This buffer won't
1060 // be used for anything else since buf_size==0.
1061 buf = new byte [1];
1063 else {
1064 if (size <= 0)
1065 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1066 if (size < 8)
1067 size = 8;
1068 buf = new byte [size];
1071 buf_size = size;
1072 buf_start = 0;
1073 buf_offset = buf_length = 0;
1074 buf_dirty = false;
1077 // fields
1079 internal const int DefaultBufferSize = 8192;
1081 private FileAccess access;
1082 private bool owner;
1083 private bool async;
1084 private bool canseek;
1085 private long append_startpos;
1086 private bool anonymous;
1088 private byte [] buf; // the buffer
1089 private int buf_size; // capacity in bytes
1090 private int buf_length; // number of valid bytes in buffer
1091 private int buf_offset; // position of next byte
1092 private bool buf_dirty; // true if buffer has been written to
1093 private long buf_start; // location of buffer in file
1094 private string name = "[Unknown]"; // name of file.
1096 IntPtr handle; // handle to underlying file