// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (C) 2001-2003 Ximian, Inc. http://www.ximian.com
-// (c) 2004 Novell, Inc. (http://www.novell.com)
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
using System.Collections;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
+using System.Security.Permissions;
using System.Threading;
+#if NET_2_0
+using Microsoft.Win32.SafeHandles;
+#endif
+
namespace System.IO
{
public class FileStream : Stream
public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
: this (handle, access, ownsHandle, bufferSize, isAsync, false) {}
+ [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
internal FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync, bool noBuffering)
{
this.handle = MonoIO.InvalidHandle;
this.canseek = true;
} else {
this.canseek = false;
+ noBuffering = true;
+ bufferSize = 0;
}
this.handle = handle;
this.access = access;
this.owner = ownsHandle;
this.async = isAsync;
-
- if (isAsync && MonoIO.SupportsAsync)
- ThreadPool.BindHandle (handle);
+ this.anonymous = false;
InitBuffer (bufferSize, noBuffering);
// construct from filename
public FileStream (string name, FileMode mode)
- : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false) { }
+ : this (name, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read, DefaultBufferSize, false, false)
+ {
+ }
public FileStream (string name, FileMode mode, FileAccess access)
- : this (name, mode, access, FileShare.ReadWrite, DefaultBufferSize, false) { }
+ : this (name, mode, access, access == FileAccess.Write ? FileShare.None : FileShare.Read, DefaultBufferSize, false, false)
+ {
+ }
public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
- : this (name, mode, access, share, DefaultBufferSize, false) { }
+ : this (name, mode, access, share, DefaultBufferSize, false, false)
+ {
+ }
public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
- : this (name, mode, access, share, bufferSize, false) { }
+ : this (name, mode, access, share, bufferSize, false, false)
+ {
+ }
public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
+ : this (name, mode, access, share, bufferSize, isAsync, false)
+ {
+ }
+
+ internal FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync, bool anonymous)
{
if (name == null) {
throw new ArgumentNullException ("name");
}
- if (name == "") {
+ if (name.Length == 0) {
throw new ArgumentException ("Name is empty");
}
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException ("Positive number required.");
+
if (mode < FileMode.CreateNew || mode > FileMode.Append)
throw new ArgumentOutOfRangeException ("mode");
}
if (Directory.Exists (name)) {
- throw new UnauthorizedAccessException ("Access to the path '" + Path.GetFullPath (name) + "' is denied.");
+ // don't leak the path information for isolated storage
+ string msg = Locale.GetText ("Access to the path '{0}' is denied.");
+ string fname = (anonymous) ? Path.GetFileName (name) : Path.GetFullPath (name);
+ throw new UnauthorizedAccessException (String.Format (msg, fname));
}
/* Append streams can't be read (see FileMode
(mode != FileMode.Open && mode != FileMode.OpenOrCreate))
throw new ArgumentException ("access and mode not compatible");
+ string dname = Path.GetDirectoryName (name);
+ if (dname.Length > 0) {
+ string fp = Path.GetFullPath (dname);
+ if (!Directory.Exists (fp)) {
+ // don't leak the path information for isolated storage
+ string msg = Locale.GetText ("Could not find a part of the path \"{0}\".");
+ string fname = (anonymous) ? dname : fp;
+ throw new DirectoryNotFoundException (String.Format (msg, fname));
+ }
+ }
+
if (access == FileAccess.Read && mode != FileMode.Create && mode != FileMode.OpenOrCreate &&
- mode != FileMode.CreateNew && !File.Exists (name))
- throw new FileNotFoundException ("Could not find file \"" + name + "\".");
-
- if (mode == FileMode.CreateNew) {
- string dname = Path.GetDirectoryName (name);
- string fp = null; ;
- if (dname != "" && !Directory.Exists ((fp = Path.GetFullPath (dname))))
- throw new DirectoryNotFoundException ("Could not find a part of " +
- "the path \"" + fp + "\".");
+ mode != FileMode.CreateNew && !File.Exists (name)) {
+ // don't leak the path information for isolated storage
+ string msg = Locale.GetText ("Could not find file \"{0}\".");
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw new FileNotFoundException (String.Format (msg, fname), fname);
}
- this.name = name;
+ // IsolatedStorage needs to keep the Name property to the default "[Unknown]"
+ if (!anonymous)
+ this.name = name;
// TODO: demand permissions
MonoIOError error;
- bool openAsync = (isAsync && MonoIO.SupportsAsync);
- this.handle = MonoIO.Open (name, mode, access, share, openAsync, out error);
+ this.handle = MonoIO.Open (name, mode, access, share, false, out error);
if (handle == MonoIO.InvalidHandle) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
this.access = access;
this.owner = true;
+ this.anonymous = anonymous;
/* Can we open non-files by name? */
if (MonoIO.GetFileType (handle, out error) == MonoFileType.Disk) {
this.canseek = true;
this.async = isAsync;
- if (openAsync)
- ThreadPool.BindHandle (handle);
} else {
this.canseek = false;
this.async = false;
if (handle == MonoIO.InvalidHandle)
throw new ObjectDisposedException ("Stream has been closed");
- if (!canseek)
+ if (!CanSeek)
throw new NotSupportedException ("The stream does not support seeking");
// Buffered data might change the length of the stream
length = MonoIO.GetLength (handle, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name,
- error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
return(length);
}
public virtual IntPtr Handle {
+ [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
+ [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
get {
return handle;
}
}
+#if NET_2_0
+ public virtual SafeFileHandle SafeFileHandle {
+ [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
+ [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
+ get { throw new NotImplementedException (); }
+ }
+#endif
+
// methods
public override int ReadByte ()
if (buf_offset == buf_size)
FlushBuffer ();
+ if (buf_size == 0) { // No buffering
+ buf [0] = value;
+ buf_dirty = true;
+ buf_length = 1;
+ FlushBuffer ();
+ return;
+ }
+
buf [buf_offset ++] = value;
if (buf_offset > buf_length)
buf_length = buf_offset;
if (!async)
return base.BeginRead (buffer, offset, count, cback, state);
- if (!MonoIO.SupportsAsync) {
- ReadDelegate r = new ReadDelegate (ReadInternal);
- return r.BeginInvoke (buffer, offset, count, cback, state);
- }
-
- FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
- result.Count = count;
- result.OriginalCount = count;
- int buffered = ReadSegment (buffer, offset, count);
- if (buffered >= count) {
- result.SetComplete (null, buffered, true);
- return result;
- }
-
- result.Buffer = buffer;
- result.Offset = offset + buffered;
- result.Count -= buffered;
-
- KeepReference (result);
- MonoIO.BeginRead (handle, result);
-
- return result;
+ ReadDelegate r = new ReadDelegate (ReadInternal);
+ return r.BeginInvoke (buffer, offset, count, cback, state);
}
public override int EndRead (IAsyncResult async_result)
if (!async)
return base.EndRead (async_result);
- if (!MonoIO.SupportsAsync) {
- AsyncResult ares = async_result as AsyncResult;
- if (ares == null)
- throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
- ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
- if (r == null)
- throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
- return r.EndInvoke (async_result);
- }
-
- FileStreamAsyncResult result = async_result as FileStreamAsyncResult;
- if (result == null || result.BytesRead == -1)
+ AsyncResult ares = async_result as AsyncResult;
+ if (ares == null)
throw new ArgumentException ("Invalid IAsyncResult", "async_result");
- RemoveReference (result);
- if (result.Done)
- throw new InvalidOperationException ("EndRead already called.");
-
- result.Done = true;
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne ();
-
- if (result.Exception != null)
- throw result.Exception;
+ ReadDelegate r = ares.AsyncDelegate as ReadDelegate;
+ if (r == null)
+ throw new ArgumentException ("Invalid IAsyncResult", "async_result");
- buf_start += result.BytesRead;
- return result.OriginalCount - result.Count + result.BytesRead;
+ return r.EndInvoke (async_result);
}
public override void Write (byte[] src, int src_offset, int count)
MonoIO.Write (handle, src, src_offset, count, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name,
- error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
buf_start += count;
if (!async)
return base.BeginWrite (buffer, offset, count, cback, state);
- byte [] bytes;
- int buffered = 0;
FileStreamAsyncResult result = new FileStreamAsyncResult (cback, state);
result.BytesRead = -1;
result.Count = count;
if (buf_dirty) {
MemoryStream ms = new MemoryStream ();
FlushBufferToStream (ms);
- buffered = (int) ms.Length;
ms.Write (buffer, offset, count);
- bytes = ms.GetBuffer ();
offset = 0;
count = (int) ms.Length;
- } else {
- bytes = buffer;
- }
-
- if (!MonoIO.SupportsAsync) {
- WriteDelegate w = new WriteDelegate (WriteInternal);
- return w.BeginInvoke (buffer, offset, count, cback, state);
}
- if (buffered >= count) {
- result.SetComplete (null, buffered, true);
- return result;
- }
-
- result.Buffer = buffer;
- result.Offset = offset;
- result.Count = count;
-
- KeepReference (result);
- MonoIO.BeginWrite (handle, result);
-
- return result;
+ WriteDelegate w = new WriteDelegate (WriteInternal);
+ return w.BeginInvoke (buffer, offset, count, cback, state);
}
public override void EndWrite (IAsyncResult async_result)
return;
}
- if (!MonoIO.SupportsAsync) {
- AsyncResult ares = async_result as AsyncResult;
- if (ares == null)
- throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
- WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
- if (w == null)
- throw new ArgumentException ("Invalid IAsyncResult", "async_result");
-
- w.EndInvoke (async_result);
- return;
- }
-
- FileStreamAsyncResult result = async_result as FileStreamAsyncResult;
- if (result == null || result.BytesRead != -1)
+ AsyncResult ares = async_result as AsyncResult;
+ if (ares == null)
throw new ArgumentException ("Invalid IAsyncResult", "async_result");
- RemoveReference (result);
- if (result.Done)
- throw new InvalidOperationException ("EndWrite already called.");
-
- result.Done = true;
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne ();
-
- if (result.Exception != null)
- throw result.Exception;
+ WriteDelegate w = ares.AsyncDelegate as WriteDelegate;
+ if (w == null)
+ throw new ArgumentException ("Invalid IAsyncResult", "async_result");
- buf_start += result.Count;
- buf_offset = buf_length = 0;
+ w.EndInvoke (async_result);
+ return;
}
public override long Seek (long offset, SeekOrigin origin)
out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
return(buf_start);
MonoIO.SetLength (handle, length, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
if (Position > length)
MonoIO.Lock (handle, position, length, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
}
MonoIO.Unlock (handle, position, length, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
}
MonoIO.Close (handle, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
handle = MonoIO.InvalidHandle;
SeekOrigin.Begin,
out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
}
st.Write (buf, 0, buf_length);
SeekOrigin.Begin,
out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
}
MonoIO.Write (handle, buf, 0,
buf_length, out error);
if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
}
if (error == MonoIOError.ERROR_BROKEN_PIPE) {
amount = 0; // might not be needed, but well...
} else if (error != MonoIOError.ERROR_SUCCESS) {
- throw MonoIO.GetException (name, error);
+ // don't leak the path information for isolated storage
+ string fname = (anonymous) ? Path.GetFileName (name) : name;
+ throw MonoIO.GetException (fname, error);
}
/* Check for read error */
buf_dirty = false;
}
- static void KeepReference (object o)
- {
- lock (typeof (FileStream)) {
- if (asyncObjects == null)
- asyncObjects = new Hashtable ();
-
- asyncObjects [o] = o;
- }
- }
-
- static void RemoveReference (object o)
- {
- lock (typeof (FileStream)) {
- if (asyncObjects == null)
- return;
-
- asyncObjects.Remove (o);
- }
- }
-
// fields
- const int DefaultBufferSize = 8192;
- private static Hashtable asyncObjects;
+ internal const int DefaultBufferSize = 8192;
private FileAccess access;
private bool owner;
private bool async;
private bool canseek;
private long append_startpos;
-
+ private bool anonymous;
private byte [] buf; // the buffer
private int buf_size; // capacity in bytes