using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Security.Permissions;
namespace Trinet.Core.IO.Ntfs
{
///
/// Represents the details of an alternative data stream.
///
[DebuggerDisplay("{FullPath}")]
public sealed class AlternateDataStreamInfo : IEquatable
{
#region Private Data
private readonly string _fullPath;
private readonly string _filePath;
private readonly string _streamName;
private readonly FileStreamType _streamType;
private readonly FileStreamAttributes _attributes;
private readonly long _size;
private readonly bool _exists;
#endregion
#region Constructor
///
/// Initializes a new instance of the class.
///
///
/// The full path of the file.
/// This argument must not be .
///
///
/// The containing the stream information.
///
internal AlternateDataStreamInfo(string filePath, SafeNativeMethods.Win32StreamInfo info)
{
_filePath = filePath;
_streamName = info.StreamName;
_streamType = info.StreamType;
_attributes = info.StreamAttributes;
_size = info.StreamSize;
_exists = true;
_fullPath = SafeNativeMethods.BuildStreamPath(_filePath, _streamName);
}
///
/// Initializes a new instance of the class.
///
///
/// The full path of the file.
/// This argument must not be .
///
///
/// The name of the stream
/// This argument must not be .
///
///
/// The full path of the stream.
/// If this argument is , it will be generated from the
/// and arguments.
///
///
/// if the stream exists;
/// otherwise, .
///
internal AlternateDataStreamInfo(string filePath, string streamName, string fullPath, bool exists)
{
if (string.IsNullOrEmpty(fullPath)) fullPath = SafeNativeMethods.BuildStreamPath(filePath, streamName);
_streamType = FileStreamType.AlternateDataStream;
_filePath = filePath;
_streamName = streamName;
_fullPath = fullPath;
_exists = exists;
if (_exists)
{
_size = SafeNativeMethods.GetFileSize(_fullPath);
}
}
#endregion
#region Properties
///
/// Returns the full path of this stream.
///
///
/// The full path of this stream.
///
public string FullPath
{
get { return _fullPath; }
}
///
/// Returns the full path of the file which contains the stream.
///
///
/// The full file-system path of the file which contains the stream.
///
public string FilePath
{
get { return _filePath; }
}
///
/// Returns the name of the stream.
///
///
/// The name of the stream.
///
public string Name
{
get { return _streamName; }
}
///
/// Returns a flag indicating whether the specified stream exists.
///
///
/// if the stream exists;
/// otherwise, .
///
public bool Exists
{
get { return _exists; }
}
///
/// Returns the size of the stream, in bytes.
///
///
/// The size of the stream, in bytes.
///
public long Size
{
get { return _size; }
}
///
/// Returns the type of data.
///
///
/// One of the values.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public FileStreamType StreamType
{
get { return _streamType; }
}
///
/// Returns attributes of the data stream.
///
///
/// A combination of values.
///
[EditorBrowsable(EditorBrowsableState.Advanced)]
public FileStreamAttributes Attributes
{
get { return _attributes; }
}
#endregion
#region Methods
#region -IEquatable
///
/// Returns a that represents the current instance.
///
///
/// A that represents the current instance.
///
public override string ToString()
{
return this.FullPath;
}
///
/// Serves as a hash function for a particular type.
///
///
/// A hash code for the current .
///
public override int GetHashCode()
{
var comparer = StringComparer.OrdinalIgnoreCase;
return comparer.GetHashCode(_filePath ?? string.Empty)
^ comparer.GetHashCode(_streamName ?? string.Empty);
}
///
/// Indicates whether the current object is equal to another object of the same type.
///
///
/// An object to compare with this object.
///
///
/// if the current object is equal to the parameter;
/// otherwise, .
///
public override bool Equals(object obj)
{
if (object.ReferenceEquals(null, obj)) return false;
if (object.ReferenceEquals(this, obj)) return true;
AlternateDataStreamInfo other = obj as AlternateDataStreamInfo;
if (!object.ReferenceEquals(null, other)) return this.Equals(other);
return false;
}
///
/// Returns a value indicating whether
/// this instance is equal to another instance.
///
///
/// The instance to compare to.
///
///
/// if the current object is equal to the parameter;
/// otherwise, .
///
public bool Equals(AlternateDataStreamInfo other)
{
if (object.ReferenceEquals(null, other)) return false;
if (object.ReferenceEquals(this, other)) return true;
var comparer = StringComparer.OrdinalIgnoreCase;
return comparer.Equals(this._filePath ?? string.Empty, other._filePath ?? string.Empty)
&& comparer.Equals(this._streamName ?? string.Empty, other._streamName ?? string.Empty);
}
///
/// The equality operator.
///
///
/// The first object.
///
///
/// The second object.
///
///
/// if the two objects are equal;
/// otherwise, .
///
public static bool operator ==(AlternateDataStreamInfo first, AlternateDataStreamInfo second)
{
if (object.ReferenceEquals(first, second)) return true;
if (object.ReferenceEquals(null, first)) return false;
if (object.ReferenceEquals(null, second)) return false;
return first.Equals(second);
}
///
/// The inequality operator.
///
///
/// The first object.
///
///
/// The second object.
///
///
/// if the two objects are not equal;
/// otherwise, .
///
public static bool operator !=(AlternateDataStreamInfo first, AlternateDataStreamInfo second)
{
if (object.ReferenceEquals(first, second)) return false;
if (object.ReferenceEquals(null, first)) return true;
if (object.ReferenceEquals(null, second)) return true;
return !first.Equals(second);
}
#endregion
#region -Delete
///
/// Deletes this stream from the parent file.
///
///
/// if the stream was deleted;
/// otherwise, .
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
public bool Delete()
{
const FileIOPermissionAccess permAccess = FileIOPermissionAccess.Write;
new FileIOPermission(permAccess, _filePath).Demand();
return SafeNativeMethods.SafeDeleteFile(this.FullPath);
}
#endregion
#region -Open
///
/// Calculates the access to demand.
///
///
/// The .
///
///
/// The .
///
///
/// The .
///
private static FileIOPermissionAccess CalculateAccess(FileMode mode, FileAccess access)
{
FileIOPermissionAccess permAccess = FileIOPermissionAccess.NoAccess;
switch (mode)
{
case FileMode.Append:
permAccess = FileIOPermissionAccess.Append;
break;
case FileMode.Create:
case FileMode.CreateNew:
case FileMode.OpenOrCreate:
case FileMode.Truncate:
permAccess = FileIOPermissionAccess.Write;
break;
case FileMode.Open:
permAccess = FileIOPermissionAccess.Read;
break;
}
switch (access)
{
case FileAccess.ReadWrite:
permAccess |= FileIOPermissionAccess.Write;
permAccess |= FileIOPermissionAccess.Read;
break;
case FileAccess.Write:
permAccess |= FileIOPermissionAccess.Write;
break;
case FileAccess.Read:
permAccess |= FileIOPermissionAccess.Read;
break;
}
return permAccess;
}
///
/// Opens this alternate data stream.
///
///
/// A value that specifies whether a stream is created if one does not exist,
/// and determines whether the contents of existing streams are retained or overwritten.
///
///
/// A value that specifies the operations that can be performed on the stream.
///
///
/// A value specifying the type of access other threads have to the file.
///
///
/// The size of the buffer to use.
///
///
/// to enable async-IO;
/// otherwise, .
///
///
/// A for this alternate data stream.
///
///
/// is less than or equal to zero.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream Open(FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
{
if (0 >= bufferSize) throw new ArgumentOutOfRangeException("bufferSize", bufferSize, null);
FileIOPermissionAccess permAccess = CalculateAccess(mode, access);
new FileIOPermission(permAccess, _filePath).Demand();
SafeNativeMethods.NativeFileFlags flags = useAsync ? SafeNativeMethods.NativeFileFlags.Overlapped : 0;
var handle = SafeNativeMethods.SafeCreateFile(this.FullPath, access.ToNative(), share, IntPtr.Zero, mode, flags, IntPtr.Zero);
if (handle.IsInvalid) SafeNativeMethods.ThrowLastIOError(this.FullPath);
return new FileStream(handle, access, bufferSize, useAsync);
}
///
/// Opens this alternate data stream.
///
///
/// A value that specifies whether a stream is created if one does not exist,
/// and determines whether the contents of existing streams are retained or overwritten.
///
///
/// A value that specifies the operations that can be performed on the stream.
///
///
/// A value specifying the type of access other threads have to the file.
///
///
/// The size of the buffer to use.
///
///
/// A for this alternate data stream.
///
///
/// is less than or equal to zero.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream Open(FileMode mode, FileAccess access, FileShare share, int bufferSize)
{
return this.Open(mode, access, share, bufferSize, false);
}
///
/// Opens this alternate data stream.
///
///
/// A value that specifies whether a stream is created if one does not exist,
/// and determines whether the contents of existing streams are retained or overwritten.
///
///
/// A value that specifies the operations that can be performed on the stream.
///
///
/// A value specifying the type of access other threads have to the file.
///
///
/// A for this alternate data stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream Open(FileMode mode, FileAccess access, FileShare share)
{
return this.Open(mode, access, share, SafeNativeMethods.DefaultBufferSize, false);
}
///
/// Opens this alternate data stream.
///
///
/// A value that specifies whether a stream is created if one does not exist,
/// and determines whether the contents of existing streams are retained or overwritten.
///
///
/// A value that specifies the operations that can be performed on the stream.
///
///
/// A for this alternate data stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream Open(FileMode mode, FileAccess access)
{
return this.Open(mode, access, FileShare.None, SafeNativeMethods.DefaultBufferSize, false);
}
///
/// Opens this alternate data stream.
///
///
/// A value that specifies whether a stream is created if one does not exist,
/// and determines whether the contents of existing streams are retained or overwritten.
///
///
/// A for this alternate data stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream Open(FileMode mode)
{
FileAccess access = (FileMode.Append == mode) ? FileAccess.Write : FileAccess.ReadWrite;
return this.Open(mode, access, FileShare.None, SafeNativeMethods.DefaultBufferSize, false);
}
#endregion
#region -OpenRead / OpenWrite / OpenText
///
/// Opens this stream for reading.
///
///
/// A read-only for this stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream OpenRead()
{
return this.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
}
///
/// Opens this stream for writing.
///
///
/// A write-only for this stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public FileStream OpenWrite()
{
return this.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
}
///
/// Opens this stream as a text file.
///
///
/// A which can be used to read the contents of this stream.
///
///
/// The caller does not have the required permission.
///
///
/// The caller does not have the required permission, or the file is read-only.
///
///
/// The specified file is in use.
///
///
/// The path of the stream is invalid.
///
///
/// There was an error opening the stream.
///
public StreamReader OpenText()
{
Stream fileStream = this.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
return new StreamReader(fileStream);
}
#endregion
#endregion
}
}