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 } }