2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / corlib / System.IO / FileStream.cs
blobd3f4fb0b1f3218e3584af20cfc0b57c8c037f495
1 //
2 // System.IO.FileStream.cs
3 //
4 // Authors:
5 // Dietmar Maurer (dietmar@ximian.com)
6 // Dan Lewis (dihlewis@yahoo.co.uk)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // Marek Safar (marek.safar@gmail.com)
9 //
10 // (C) 2001-2003 Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004-2005, 2008, 2010 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Globalization;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Remoting.Messaging;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Threading;
42 using Microsoft.Win32.SafeHandles;
43 #if NET_2_1
44 using System.IO.IsolatedStorage;
45 #else
46 using System.Security.AccessControl;
47 #endif
49 namespace System.IO
51 [ComVisible (true)]
52 public class FileStream : Stream
54 // construct from handle
56 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
57 public FileStream (IntPtr handle, FileAccess access)
58 : this (handle, access, true, DefaultBufferSize, false) {}
60 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access) instead")]
61 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
62 : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
64 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead")]
65 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
66 : this (handle, access, ownsHandle, bufferSize, false) {}
68 [Obsolete ("Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead")]
69 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
70 : this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
72 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
73 internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool isZeroSize)
75 this.handle = MonoIO.InvalidHandle;
76 if (handle == this.handle)
77 throw new ArgumentException ("handle", Locale.GetText ("Invalid."));
79 if (access < FileAccess.Read || access > FileAccess.ReadWrite)
80 throw new ArgumentOutOfRangeException ("access");
82 MonoIOError error;
83 MonoFileType ftype = MonoIO.GetFileType (handle, out error);
85 if (error != MonoIOError.ERROR_SUCCESS) {
86 throw MonoIO.GetException (name, error);
89 if (ftype == MonoFileType.Unknown) {
90 throw new IOException ("Invalid handle.");
91 } else if (ftype == MonoFileType.Disk) {
92 this.canseek = true;
93 } else {
94 this.canseek = false;
97 this.handle = handle;
98 this.access = access;
99 this.owner = ownsHandle;
100 this.async = isAsync;
101 #if MOONLIGHT
102 // default the browser to 'all' anonymous files and let other usage (like smcs) with 'normal'
103 // (i.e. non-anonymous except for isolated storage) files and paths
104 this.anonymous = SecurityManager.SecurityEnabled;
105 #else
106 this.anonymous = false;
107 #endif
108 if (isZeroSize)
109 bufferSize = 1;
111 InitBuffer (bufferSize);
113 if (canseek) {
114 buf_start = MonoIO.Seek (handle, 0, SeekOrigin.Current, out error);
115 if (error != MonoIOError.ERROR_SUCCESS) {
116 throw MonoIO.GetException (name, error);
120 /* Can't set append mode */
121 this.append_startpos=0;
124 // construct from filename
126 public FileStream (string path, FileMode mode)
127 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, FileOptions.None)
131 public FileStream (string path, FileMode mode, FileAccess access)
132 : this (path, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
136 public FileStream (string path, FileMode mode, FileAccess access, FileShare share)
137 : this (path, mode, access, share, DefaultBufferSize, false, FileOptions.None)
141 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
142 : this (path, mode, access, share, bufferSize, false, FileOptions.None)
146 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
147 : this (path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
151 public FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
152 : this (path, mode, access, share, bufferSize, false, options)
156 #if !NET_2_1
157 public FileStream (SafeFileHandle handle, FileAccess access)
158 :this(handle, access, DefaultBufferSize, false)
162 public FileStream (SafeFileHandle handle, FileAccess access,
163 int bufferSize)
164 :this(handle, access, bufferSize, false)
168 [MonoLimitationAttribute("Need to use SafeFileHandle instead of underlying handle")]
169 public FileStream (SafeFileHandle handle, FileAccess access,
170 int bufferSize, bool isAsync)
171 :this (handle.DangerousGetHandle (), access, false, bufferSize, isAsync)
173 this.safeHandle = handle;
176 [MonoLimitation ("This ignores the rights parameter")]
177 public FileStream (string path, FileMode mode,
178 FileSystemRights rights, FileShare share,
179 int bufferSize, FileOptions options)
180 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
184 [MonoLimitation ("This ignores the rights and fileSecurity parameters")]
185 public FileStream (string path, FileMode mode,
186 FileSystemRights rights, FileShare share,
187 int bufferSize, FileOptions options,
188 FileSecurity fileSecurity)
189 : this (path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), share, bufferSize, false, options)
192 #endif
194 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
195 : this (path, mode, access, share, bufferSize, anonymous, isAsync ? FileOptions.Asynchronous : FileOptions.None)
199 internal FileStream (string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool anonymous, FileOptions options)
201 if (path == null) {
202 throw new ArgumentNullException ("path");
205 if (path.Length == 0) {
206 throw new ArgumentException ("Path is empty");
209 // ignore the Inheritable flag
210 share &= ~FileShare.Inheritable;
212 if (bufferSize <= 0)
213 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
215 if (mode < FileMode.CreateNew || mode > FileMode.Append) {
216 #if NET_2_1
217 if (anonymous)
218 throw new ArgumentException ("mode", "Enum value was out of legal range.");
219 else
220 #endif
221 throw new ArgumentOutOfRangeException ("mode", "Enum value was out of legal range.");
224 if (access < FileAccess.Read || access > FileAccess.ReadWrite) {
225 #if NET_2_1
226 if (anonymous)
227 throw new IsolatedStorageException ("Enum value for FileAccess was out of legal range.");
228 else
229 #endif
230 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
233 if (share < FileShare.None || share > (FileShare.ReadWrite | FileShare.Delete)) {
234 #if NET_2_1
235 if (anonymous)
236 throw new IsolatedStorageException ("Enum value for FileShare was out of legal range.");
237 else
238 #endif
239 throw new ArgumentOutOfRangeException ("share", "Enum value was out of legal range.");
242 if (path.IndexOfAny (Path.InvalidPathChars) != -1) {
243 throw new ArgumentException ("Name has invalid chars");
246 if (Directory.Exists (path)) {
247 // don't leak the path information for isolated storage
248 string msg = Locale.GetText ("Access to the path '{0}' is denied.");
249 throw new UnauthorizedAccessException (String.Format (msg, GetSecureFileName (path, false)));
252 /* Append streams can't be read (see FileMode
253 * docs)
255 if (mode==FileMode.Append &&
256 (access&FileAccess.Read)==FileAccess.Read) {
257 throw new ArgumentException("Append access can be requested only in write-only mode.");
260 if ((access & FileAccess.Write) == 0 &&
261 (mode != FileMode.Open && mode != FileMode.OpenOrCreate)) {
262 string msg = Locale.GetText ("Combining FileMode: {0} with " +
263 "FileAccess: {1} is invalid.");
264 throw new ArgumentException (string.Format (msg, access, mode));
267 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
269 string dname;
270 if (Path.DirectorySeparatorChar != '/' && path.IndexOf ('/') >= 0)
271 dname = Path.GetDirectoryName (Path.GetFullPath (path));
272 else
273 dname = Path.GetDirectoryName (path);
274 if (dname.Length > 0) {
275 string fp = Path.GetFullPath (dname);
276 if (!Directory.Exists (fp)) {
277 // don't leak the path information for isolated storage
278 string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
279 string fname = (anonymous) ? dname : Path.GetFullPath (path);
280 #if NET_2_1
281 // don't use GetSecureFileName for the directory name
282 throw new IsolatedStorageException (String.Format (msg, fname));
283 #else
284 throw new DirectoryNotFoundException (String.Format (msg, fname));
285 #endif
289 if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
290 mode != FileMode.CreateNew && !File.Exists (path)) {
291 // don't leak the path information for isolated storage
292 string msg = Locale.GetText ("Could not find file \"{0}\".");
293 string fname = GetSecureFileName (path);
294 #if NET_2_1
295 throw new IsolatedStorageException (String.Format (msg, fname));
296 #else
297 throw new FileNotFoundException (String.Format (msg, fname), fname);
298 #endif
301 // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
302 if (!anonymous)
303 this.name = path;
305 // TODO: demand permissions
307 MonoIOError error;
309 this.handle = MonoIO.Open (path, mode, access, share, options, out error);
310 if (handle == MonoIO.InvalidHandle) {
311 // don't leak the path information for isolated storage
312 throw MonoIO.GetException (GetSecureFileName (path), error);
315 this.access = access;
316 this.owner = true;
317 this.anonymous = anonymous;
319 /* Can we open non-files by name? */
321 if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
322 this.canseek = true;
323 this.async = (options & FileOptions.Asynchronous) != 0;
324 } else {
325 this.canseek = false;
326 this.async = false;
330 if (access == FileAccess.Read && canseek && (bufferSize == DefaultBufferSize)) {
331 /* Avoid allocating a large buffer for small files */
332 long len = Length;
333 if (bufferSize > len) {
334 bufferSize = (int)(len < 1000 ? 1000 : len);
338 InitBuffer (bufferSize);
340 if (mode==FileMode.Append) {
341 this.Seek (0, SeekOrigin.End);
342 this.append_startpos=this.Position;
343 } else {
344 this.append_startpos=0;
348 // properties
350 public override bool CanRead {
351 get {
352 return access == FileAccess.Read ||
353 access == FileAccess.ReadWrite;
357 public override bool CanWrite {
358 get {
359 return access == FileAccess.Write ||
360 access == FileAccess.ReadWrite;
364 public override bool CanSeek {
365 get {
366 return(canseek);
370 public virtual bool IsAsync {
371 get {
372 return (async);
376 public string Name {
377 get {
378 #if MOONLIGHT
379 return SecurityManager.CheckElevatedPermissions () ? name : "[Unknown]";
380 #else
381 return name;
382 #endif
386 public override long Length {
387 get {
388 if (handle == MonoIO.InvalidHandle)
389 throw new ObjectDisposedException ("Stream has been closed");
391 if (!CanSeek)
392 throw new NotSupportedException ("The stream does not support seeking");
394 // Buffered data might change the length of the stream
395 FlushBufferIfDirty ();
397 MonoIOError error;
398 long length;
400 length = MonoIO.GetLength (handle, out error);
401 if (error != MonoIOError.ERROR_SUCCESS) {
402 // don't leak the path information for isolated storage
403 throw MonoIO.GetException (GetSecureFileName (name), error);
406 return(length);
410 public override long Position {
411 get {
412 if (handle == MonoIO.InvalidHandle)
413 throw new ObjectDisposedException ("Stream has been closed");
415 if(CanSeek == false)
416 throw new NotSupportedException("The stream does not support seeking");
418 return(buf_start + buf_offset);
420 set {
421 if (handle == MonoIO.InvalidHandle)
422 throw new ObjectDisposedException ("Stream has been closed");
424 if(CanSeek == false) {
425 throw new NotSupportedException("The stream does not support seeking");
428 if(value < 0) {
429 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
432 Seek (value, SeekOrigin.Begin);
436 [Obsolete ("Use SafeFileHandle instead")]
437 public virtual IntPtr Handle {
438 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
439 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
440 get {
441 return handle;
445 public virtual SafeFileHandle SafeFileHandle {
446 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
447 [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
448 get {
449 SafeFileHandle ret;
451 if (safeHandle != null)
452 ret = safeHandle;
453 else
454 ret = new SafeFileHandle (handle, owner);
456 FlushBuffer ();
457 return ret;
461 // methods
463 public override int ReadByte ()
465 if (handle == MonoIO.InvalidHandle)
466 throw new ObjectDisposedException ("Stream has been closed");
468 if (!CanRead)
469 throw new NotSupportedException ("Stream does not support reading");
471 if (buf_size == 0) {
472 int n = ReadData (handle, buf, 0, 1);
473 if (n == 0) return -1;
474 else return buf[0];
476 else if (buf_offset >= buf_length) {
477 RefillBuffer ();
479 if (buf_length == 0)
480 return -1;
483 return buf [buf_offset ++];
486 public override void WriteByte (byte value)
488 if (handle == MonoIO.InvalidHandle)
489 throw new ObjectDisposedException ("Stream has been closed");
491 if (!CanWrite)
492 throw new NotSupportedException ("Stream does not support writing");
494 if (buf_offset == buf_size)
495 FlushBuffer ();
497 if (buf_size == 0) { // No buffering
498 buf [0] = value;
499 buf_dirty = true;
500 buf_length = 1;
501 FlushBuffer ();
502 return;
505 buf [buf_offset ++] = value;
506 if (buf_offset > buf_length)
507 buf_length = buf_offset;
509 buf_dirty = true;
512 public override int Read ([In,Out] byte[] array, int offset, int count)
514 if (handle == MonoIO.InvalidHandle)
515 throw new ObjectDisposedException ("Stream has been closed");
516 if (array == null)
517 throw new ArgumentNullException ("array");
518 if (!CanRead)
519 throw new NotSupportedException ("Stream does not support reading");
520 int len = array.Length;
521 if (offset < 0)
522 throw new ArgumentOutOfRangeException ("offset", "< 0");
523 if (count < 0)
524 throw new ArgumentOutOfRangeException ("count", "< 0");
525 if (offset > len)
526 throw new ArgumentException ("destination offset is beyond array size");
527 // reordered to avoid possible integer overflow
528 if (offset > len - count)
529 throw new ArgumentException ("Reading would overrun buffer");
531 if (async) {
532 IAsyncResult ares = BeginRead (array, offset, count, null, null);
533 return EndRead (ares);
536 return ReadInternal (array, offset, count);
539 int ReadInternal (byte [] dest, int offset, int count)
541 int copied = 0;
543 int n = ReadSegment (dest, offset, count);
544 copied += n;
545 count -= n;
547 if (count == 0) {
548 /* If there was already enough
549 * buffered, no need to read
550 * more from the file.
552 return (copied);
555 if (count > buf_size) {
556 /* Read as much as we can, up
557 * to count bytes
559 FlushBuffer();
560 n = ReadData (handle, dest,
561 offset+copied,
562 count);
564 /* Make the next buffer read
565 * start from the right place
567 buf_start += n;
568 } else {
569 RefillBuffer ();
570 n = ReadSegment (dest,
571 offset+copied,
572 count);
575 copied += n;
577 return copied;
580 delegate int ReadDelegate (byte [] buffer, int offset, int count);
582 public override IAsyncResult BeginRead (byte [] array, int offset, int numBytes,
583 AsyncCallback userCallback, object stateObject)
585 if (handle == MonoIO.InvalidHandle)
586 throw new ObjectDisposedException ("Stream has been closed");
588 if (!CanRead)
589 throw new NotSupportedException ("This stream does not support reading");
591 if (array == null)
592 throw new ArgumentNullException ("array");
594 if (numBytes < 0)
595 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
597 if (offset < 0)
598 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
600 // reordered to avoid possible integer overflow
601 if (numBytes > array.Length - offset)
602 throw new ArgumentException ("Buffer too small. numBytes/offset wrong.");
604 if (!async)
605 return base.BeginRead (array, offset, numBytes, userCallback, stateObject);
607 ReadDelegate r = new ReadDelegate (ReadInternal);
608 return r.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
611 public override int EndRead (IAsyncResult asyncResult)
613 if (asyncResult == null)
614 throw new ArgumentNullException ("asyncResult");
616 if (!async)
617 return base.EndRead (asyncResult);
619 AsyncResult ares = asyncResult as AsyncResult;
620 if (ares == null)
621 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
623 ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
624 if (r == null)
625 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
627 return r.EndInvoke (asyncResult);
630 public override void Write (byte[] array, int offset, int count)
632 if (handle == MonoIO.InvalidHandle)
633 throw new ObjectDisposedException ("Stream has been closed");
634 if (array == null)
635 throw new ArgumentNullException ("array");
636 if (offset < 0)
637 throw new ArgumentOutOfRangeException ("offset", "< 0");
638 if (count < 0)
639 throw new ArgumentOutOfRangeException ("count", "< 0");
640 // ordered to avoid possible integer overflow
641 if (offset > array.Length - count)
642 throw new ArgumentException ("Reading would overrun buffer");
643 if (!CanWrite)
644 throw new NotSupportedException ("Stream does not support writing");
646 if (async) {
647 IAsyncResult ares = BeginWrite (array, offset, count, null, null);
648 EndWrite (ares);
649 return;
652 WriteInternal (array, offset, count);
655 void WriteInternal (byte [] src, int offset, int count)
657 if (count > buf_size) {
658 // shortcut for long writes
659 MonoIOError error;
661 FlushBuffer ();
662 int wcount = count;
664 while (wcount > 0){
665 int n = MonoIO.Write (handle, src, offset, wcount, out error);
666 if (error != MonoIOError.ERROR_SUCCESS)
667 throw MonoIO.GetException (GetSecureFileName (name), error);
669 wcount -= n;
670 offset += n;
672 buf_start += count;
673 } else {
675 int copied = 0;
676 while (count > 0) {
678 int n = WriteSegment (src, offset + copied, count);
679 copied += n;
680 count -= n;
682 if (count == 0) {
683 break;
686 FlushBuffer ();
691 delegate void WriteDelegate (byte [] buffer, int offset, int count);
693 public override IAsyncResult BeginWrite (byte [] array, int offset, int numBytes,
694 AsyncCallback userCallback, object stateObject)
696 if (handle == MonoIO.InvalidHandle)
697 throw new ObjectDisposedException ("Stream has been closed");
699 if (!CanWrite)
700 throw new NotSupportedException ("This stream does not support writing");
702 if (array == null)
703 throw new ArgumentNullException ("array");
705 if (numBytes < 0)
706 throw new ArgumentOutOfRangeException ("numBytes", "Must be >= 0");
708 if (offset < 0)
709 throw new ArgumentOutOfRangeException ("offset", "Must be >= 0");
711 // reordered to avoid possible integer overflow
712 if (numBytes > array.Length - offset)
713 throw new ArgumentException ("array too small. numBytes/offset wrong.");
715 if (!async)
716 return base.BeginWrite (array, offset, numBytes, userCallback, stateObject);
718 FileStreamAsyncResult result = new FileStreamAsyncResult (userCallback, stateObject);
719 result.BytesRead = -1;
720 result.Count = numBytes;
721 result.OriginalCount = numBytes;
723 if (buf_dirty) {
724 MemoryStream ms = new MemoryStream ();
725 FlushBuffer (ms);
726 ms.Write (array, offset, numBytes);
727 offset = 0;
728 numBytes = (int) ms.Length;
731 WriteDelegate w = new WriteDelegate (WriteInternal);
732 return w.BeginInvoke (array, offset, numBytes, userCallback, stateObject);
735 public override void EndWrite (IAsyncResult asyncResult)
737 if (asyncResult == null)
738 throw new ArgumentNullException ("asyncResult");
740 if (!async) {
741 base.EndWrite (asyncResult);
742 return;
745 AsyncResult ares = asyncResult as AsyncResult;
746 if (ares == null)
747 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
749 WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
750 if (w == null)
751 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
753 w.EndInvoke (asyncResult);
754 return;
757 public override long Seek (long offset, SeekOrigin origin)
759 long pos;
761 if (handle == MonoIO.InvalidHandle)
762 throw new ObjectDisposedException ("Stream has been closed");
764 // make absolute
766 if(CanSeek == false) {
767 throw new NotSupportedException("The stream does not support seeking");
770 switch (origin) {
771 case SeekOrigin.End:
772 pos = Length + offset;
773 break;
775 case SeekOrigin.Current:
776 pos = Position + offset;
777 break;
779 case SeekOrigin.Begin:
780 pos = offset;
781 break;
783 default:
784 throw new ArgumentException ("origin", "Invalid SeekOrigin");
787 if (pos < 0) {
788 /* LAMESPEC: shouldn't this be
789 * ArgumentOutOfRangeException?
791 throw new IOException("Attempted to Seek before the beginning of the stream");
794 if(pos < this.append_startpos) {
795 /* More undocumented crap */
796 throw new IOException("Can't seek back over pre-existing data in append mode");
799 FlushBuffer ();
801 MonoIOError error;
803 buf_start = MonoIO.Seek (handle, pos,
804 SeekOrigin.Begin,
805 out error);
807 if (error != MonoIOError.ERROR_SUCCESS) {
808 // don't leak the path information for isolated storage
809 throw MonoIO.GetException (GetSecureFileName (name), error);
812 return(buf_start);
815 public override void SetLength (long value)
817 if (handle == MonoIO.InvalidHandle)
818 throw new ObjectDisposedException ("Stream has been closed");
820 if(CanSeek == false)
821 throw new NotSupportedException("The stream does not support seeking");
823 if(CanWrite == false)
824 throw new NotSupportedException("The stream does not support writing");
826 if(value < 0)
827 throw new ArgumentOutOfRangeException("value is less than 0");
829 Flush ();
831 MonoIOError error;
833 MonoIO.SetLength (handle, value, out error);
834 if (error != MonoIOError.ERROR_SUCCESS) {
835 // don't leak the path information for isolated storage
836 throw MonoIO.GetException (GetSecureFileName (name), error);
839 if (Position > value)
840 Position = value;
843 public override void Flush ()
845 if (handle == MonoIO.InvalidHandle)
846 throw new ObjectDisposedException ("Stream has been closed");
848 FlushBuffer ();
851 #if NET_4_0
852 public virtual void Flush (bool flushToDisk)
854 FlushBuffer ();
856 // This does the fsync
857 if (flushToDisk){
858 MonoIOError error;
859 MonoIO.Flush (handle, out error);
862 #endif
864 public virtual void Lock (long position, long length)
866 if (handle == MonoIO.InvalidHandle)
867 throw new ObjectDisposedException ("Stream has been closed");
868 if (position < 0) {
869 throw new ArgumentOutOfRangeException ("position must not be negative");
871 if (length < 0) {
872 throw new ArgumentOutOfRangeException ("length must not be negative");
874 if (handle == MonoIO.InvalidHandle) {
875 throw new ObjectDisposedException ("Stream has been closed");
878 MonoIOError error;
880 MonoIO.Lock (handle, position, length, out error);
881 if (error != MonoIOError.ERROR_SUCCESS) {
882 // don't leak the path information for isolated storage
883 throw MonoIO.GetException (GetSecureFileName (name), error);
887 public virtual void Unlock (long position, long length)
889 if (handle == MonoIO.InvalidHandle)
890 throw new ObjectDisposedException ("Stream has been closed");
891 if (position < 0) {
892 throw new ArgumentOutOfRangeException ("position must not be negative");
894 if (length < 0) {
895 throw new ArgumentOutOfRangeException ("length must not be negative");
898 MonoIOError error;
900 MonoIO.Unlock (handle, position, length, out error);
901 if (error != MonoIOError.ERROR_SUCCESS) {
902 // don't leak the path information for isolated storage
903 throw MonoIO.GetException (GetSecureFileName (name), error);
907 // protected
909 ~FileStream ()
911 Dispose (false);
914 protected override void Dispose (bool disposing)
916 Exception exc = null;
917 if (handle != MonoIO.InvalidHandle) {
918 try {
919 FlushBuffer ();
920 } catch (Exception e) {
921 exc = e;
924 if (owner) {
925 MonoIOError error;
927 MonoIO.Close (handle, out error);
928 if (error != MonoIOError.ERROR_SUCCESS) {
929 // don't leak the path information for isolated storage
930 throw MonoIO.GetException (GetSecureFileName (name), error);
933 handle = MonoIO.InvalidHandle;
937 canseek = false;
938 access = 0;
940 if (disposing && buf != null) {
941 if (buf.Length == DefaultBufferSize && buf_recycle == null) {
942 lock (buf_recycle_lock) {
943 if (buf_recycle == null) {
944 buf_recycle = buf;
949 buf = null;
950 GC.SuppressFinalize (this);
952 if (exc != null)
953 throw exc;
956 #if !NET_2_1
957 public FileSecurity GetAccessControl ()
959 throw new NotImplementedException ();
962 public void SetAccessControl (FileSecurity fileSecurity)
964 throw new NotImplementedException ();
966 #endif
968 // private.
970 // ReadSegment, WriteSegment, FlushBuffer,
971 // RefillBuffer and ReadData should only be called
972 // when the Monitor lock is held, but these methods
973 // grab it again just to be safe.
975 private int ReadSegment (byte [] dest, int dest_offset, int count)
977 if (count > buf_length - buf_offset) {
978 count = buf_length - buf_offset;
981 if (count > 0) {
982 Buffer.BlockCopy (buf, buf_offset,
983 dest, dest_offset,
984 count);
985 buf_offset += count;
988 return(count);
991 private int WriteSegment (byte [] src, int src_offset,
992 int count)
994 if (count > buf_size - buf_offset) {
995 count = buf_size - buf_offset;
998 if (count > 0) {
999 Buffer.BlockCopy (src, src_offset,
1000 buf, buf_offset,
1001 count);
1002 buf_offset += count;
1003 if (buf_offset > buf_length) {
1004 buf_length = buf_offset;
1007 buf_dirty = true;
1010 return(count);
1013 void FlushBuffer (Stream st)
1015 if (buf_dirty) {
1016 MonoIOError error;
1018 if (CanSeek == true) {
1019 MonoIO.Seek (handle, buf_start,
1020 SeekOrigin.Begin,
1021 out error);
1022 if (error != MonoIOError.ERROR_SUCCESS) {
1023 // don't leak the path information for isolated storage
1024 throw MonoIO.GetException (GetSecureFileName (name), error);
1027 if (st == null) {
1028 int wcount = buf_length;
1029 int offset = 0;
1030 while (wcount > 0){
1031 int n = MonoIO.Write (handle, buf, 0, buf_length, out error);
1032 if (error != MonoIOError.ERROR_SUCCESS) {
1033 // don't leak the path information for isolated storage
1034 throw MonoIO.GetException (GetSecureFileName (name), error);
1036 wcount -= n;
1037 offset += n;
1039 } else {
1040 st.Write (buf, 0, buf_length);
1044 buf_start += buf_offset;
1045 buf_offset = buf_length = 0;
1046 buf_dirty = false;
1049 private void FlushBuffer ()
1051 FlushBuffer (null);
1054 private void FlushBufferIfDirty ()
1056 if (buf_dirty)
1057 FlushBuffer (null);
1060 private void RefillBuffer ()
1062 FlushBuffer (null);
1064 buf_length = ReadData (handle, buf, 0,
1065 buf_size);
1068 private int ReadData (IntPtr handle, byte[] buf, int offset,
1069 int count)
1071 MonoIOError error;
1072 int amount = 0;
1074 /* when async == true, if we get here we don't suport AIO or it's disabled
1075 * and we're using the threadpool */
1076 amount = MonoIO.Read (handle, buf, offset, count, out error);
1077 if (error == MonoIOError.ERROR_BROKEN_PIPE) {
1078 amount = 0; // might not be needed, but well...
1079 } else if (error != MonoIOError.ERROR_SUCCESS) {
1080 // don't leak the path information for isolated storage
1081 throw MonoIO.GetException (GetSecureFileName (name), error);
1084 /* Check for read error */
1085 if(amount == -1) {
1086 throw new IOException ();
1089 return(amount);
1092 void InitBuffer (int size)
1094 if (size <= 0)
1095 throw new ArgumentOutOfRangeException ("bufferSize", "Positive number required.");
1097 size = Math.Max (size, 8);
1100 // Instead of allocating a new default buffer use the
1101 // last one if there is any available
1103 if (size <= DefaultBufferSize && buf_recycle != null) {
1104 lock (buf_recycle_lock) {
1105 if (buf_recycle != null) {
1106 buf = buf_recycle;
1107 buf_recycle = null;
1112 if (buf == null)
1113 buf = new byte [size];
1114 else
1115 Array.Clear (buf, 0, size);
1117 buf_size = size;
1118 // buf_start = 0;
1119 // buf_offset = buf_length = 0;
1120 // buf_dirty = false;
1123 private string GetSecureFileName (string filename)
1125 return (anonymous) ? Path.GetFileName (filename) : Path.GetFullPath (filename);
1128 private string GetSecureFileName (string filename, bool full)
1130 return (anonymous) ? Path.GetFileName (filename) :
1131 (full) ? Path.GetFullPath (filename) : filename;
1134 // fields
1136 internal const int DefaultBufferSize = 8192;
1138 // Input buffer ready for recycling
1139 static byte[] buf_recycle;
1140 static readonly object buf_recycle_lock = new object ();
1142 private FileAccess access;
1143 private bool owner;
1144 private bool async;
1145 private bool canseek;
1146 private long append_startpos;
1147 private bool anonymous;
1149 private byte [] buf; // the buffer
1150 private int buf_size; // capacity in bytes
1151 private int buf_length; // number of valid bytes in buffer
1152 private int buf_offset; // position of next byte
1153 private bool buf_dirty; // true if buffer has been written to
1154 private long buf_start; // location of buffer in file
1155 private string name = "[Unknown]"; // name of file.
1157 IntPtr handle; // handle to underlying file
1158 SafeFileHandle safeHandle; // set only when using one of the
1159 // constructors taking SafeFileHandle