/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
namespace Alphaleonis.Win32.Filesystem
{
/// Provides the base class for both and objects.
[SerializableAttribute]
[ComVisibleAttribute(true)]
public abstract class FileSystemInfo : MarshalByRefObject
{
#region Methods
#region .NET
#region Delete
/// Deletes a file or directory.
[SecurityCritical]
public abstract void Delete();
#endregion // Delete
#region Refresh
/// Refreshes the state of the object.
///
/// FileSystemInfo.Refresh() takes a snapshot of the file from the current file system.
/// Refresh cannot correct the underlying file system even if the file system returns incorrect or outdated information.
/// This can happen on platforms such as Windows 98.
/// Calls must be made to Refresh() before attempting to get the attribute information, or the information will be
/// outdated.
///
[SecurityCritical]
protected void Refresh()
{
DataInitialised = File.FillAttributeInfoCore(Transaction, LongFullName, ref Win32AttributeData, false, false);
}
#endregion // Refresh
#region ToString
/// Returns a string that represents the current object.
///
/// ToString is the major formatting method in the .NET Framework. It converts an object to its string representation so that it is
/// suitable for display.
///
/// A string that represents this instance.
public override string ToString()
{
// "Alphaleonis.Win32.Filesystem.FileSystemInfo"
return GetType().ToString();
}
#endregion // ToString
#region Equality
/// Determines whether the specified Object is equal to the current Object.
/// Another object to compare to.
/// if the specified Object is equal to the current Object; otherwise, .
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
return false;
FileSystemInfo other = obj as FileSystemInfo;
return other != null && (other.Name != null &&
(other.FullName.Equals(FullName, StringComparison.OrdinalIgnoreCase) &&
other.Attributes.Equals(Attributes) &&
other.CreationTimeUtc.Equals(CreationTimeUtc) &&
other.LastWriteTimeUtc.Equals(LastWriteTimeUtc)));
}
// A random prime number will be picked and added to the HashCode, each time an instance is created.
[NonSerialized]
private readonly int _random = new Random().Next(0, 19);
[NonSerialized]
private static readonly int[] Primes = { 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919 };
/// Serves as a hash function for a particular type.
/// A hash code for the current Object.
public override int GetHashCode()
{
string fullName = FullName;
string name = Name;
unchecked
{
int hash = Primes[_random];
if (!Utils.IsNullOrWhiteSpace(fullName))
hash = hash * Primes[1] + fullName.GetHashCode();
if (!Utils.IsNullOrWhiteSpace(name))
hash = hash * Primes[1] + name.GetHashCode();
hash = hash * Primes[1] + Attributes.GetHashCode();
hash = hash * Primes[1] + CreationTimeUtc.GetHashCode();
hash = hash * Primes[1] + LastWriteTimeUtc.GetHashCode();
return hash;
}
}
/// Implements the operator ==
/// A.
/// B.
/// The result of the operator.
public static bool operator ==(FileSystemInfo left, FileSystemInfo right)
{
return ReferenceEquals(left, null) && ReferenceEquals(right, null) ||
!ReferenceEquals(left, null) && !ReferenceEquals(right, null) && left.Equals(right);
}
/// Implements the operator !=
/// A.
/// B.
/// The result of the operator.
public static bool operator !=(FileSystemInfo left, FileSystemInfo right)
{
return !(left == right);
}
#endregion // Equality
#endregion // .NET
#region AlphaFS
#region RefreshEntryInfo
/// Refreshes the state of the EntryInfo instance.
///
/// FileSystemInfo.RefreshEntryInfo() takes a snapshot of the file from the current file system.
/// Refresh cannot correct the underlying file system even if the file system returns incorrect or outdated information.
/// This can happen on platforms such as Windows 98.
/// Calls must be made to Refresh() before attempting to get the attribute information, or the information will be outdated.
///
[SecurityCritical]
protected void RefreshEntryInfo()
{
_entryInfo = File.GetFileSystemEntryInfoCore(IsDirectory, Transaction, LongFullName, true, PathFormat.LongFullPath);
if (_entryInfo == null)
DataInitialised = -1;
else
{
DataInitialised = 0;
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(_entryInfo.Win32FindData);
}
}
#endregion // RefreshEntryInfo
#region Reset
/// [AlphaFS] Resets the state of the file system object to uninitialized.
internal void Reset()
{
DataInitialised = -1;
}
#endregion // Reset
#region InitializeCore
/// Initializes the specified file name.
///
///
/// Specifies that is a file or directory.
/// The transaction.
/// The full path and name of the file.
/// Indicates the format of the path parameter(s).
internal void InitializeCore(bool isFolder, KernelTransaction transaction, string path, PathFormat pathFormat)
{
if (pathFormat == PathFormat.RelativePath)
Path.CheckSupportedPathFormat(path, true, true);
LongFullName = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.TrimEnd | (isFolder ? GetFullPathOptions.RemoveTrailingDirectorySeparator : 0) | GetFullPathOptions.ContinueOnNonExist);
// (Not on MSDN): .NET 4+ Trailing spaces are removed from the end of the path parameter before creating the FileSystemInfo instance.
FullPath = Path.GetRegularPathCore(LongFullName, GetFullPathOptions.None, false);
IsDirectory = isFolder;
Transaction = transaction;
OriginalPath = FullPath.Length == 2 && (FullPath[1] == Path.VolumeSeparatorChar)
? Path.CurrentDirectoryPrefix
: path;
DisplayPath = OriginalPath.Length != 2 || OriginalPath[1] != Path.VolumeSeparatorChar
? Path.GetRegularPathCore(OriginalPath, GetFullPathOptions.None, false)
: Path.CurrentDirectoryPrefix;
}
#endregion // InitializeCore
#endregion // AlphaFS
#endregion // Methods
#region Properties
#region .NET
#region Attributes
///
/// Gets or sets the attributes for the current file or directory.
///
///
/// The value of the CreationTime property is pre-cached
/// To get the latest value, call the Refresh method.
///
/// of the current .
///
///
///
///
public FileAttributes Attributes
{
[SecurityCritical]
get
{
if (DataInitialised == -1)
{
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
Refresh();
}
// MSDN: .NET 3.5+: IOException: Refresh cannot initialize the data.
if (DataInitialised != 0)
NativeError.ThrowException(DataInitialised, LongFullName);
return Win32AttributeData.dwFileAttributes;
}
[SecurityCritical]
set
{
File.SetAttributesCore(IsDirectory, Transaction, LongFullName, value, false, PathFormat.LongFullPath);
Reset();
}
}
#endregion // Attributes
#region CreationTime
/// Gets or sets the creation time of the current file or directory.
///
/// The value of the CreationTime property is pre-cached To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions whose values may not be continuously updated by
/// the operating system.
/// If the file described in the FileSystemInfo object does not exist, this property will return
/// 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
/// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time.
/// This process is known as file tunneling. As a result, it may be necessary to explicitly set the creation time of a file if you are
/// overwriting or replacing an existing file.
///
/// The creation date and time of the current object.
///
///
///
public DateTime CreationTime
{
[SecurityCritical] get { return CreationTimeUtc.ToLocalTime(); }
[SecurityCritical] set { CreationTimeUtc = value.ToUniversalTime(); }
}
#endregion // CreationTime
#region CreationTimeUtc
/// Gets or sets the creation time, in coordinated universal time (UTC), of the current file or directory.
///
/// The value of the CreationTimeUtc property is pre-cached
/// To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions
/// whose values may not be continuously updated by the operating system.
/// To get the latest value, call the Refresh method.
/// If the file described in the FileSystemInfo object does not exist, this property will return
/// 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC).
/// NTFS-formatted drives may cache file meta-info, such as file creation time, for a short period of time.
/// This process is known as file tunneling. As a result, it may be necessary to explicitly set the creation time
/// of a file if you are overwriting or replacing an existing file.
///
/// The creation date and time in UTC format of the current object.
///
///
///
[ComVisible(false)]
public DateTime CreationTimeUtc
{
[SecurityCritical]
get
{
if (DataInitialised == -1)
{
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
Refresh();
}
// MSDN: .NET 3.5+: IOException: Refresh cannot initialize the data.
if (DataInitialised != 0)
NativeError.ThrowException(DataInitialised, LongFullName);
return DateTime.FromFileTimeUtc(Win32AttributeData.ftCreationTime);
}
[SecurityCritical]
set
{
File.SetFsoDateTimeCore(IsDirectory, Transaction, LongFullName, value, null, null, false, PathFormat.LongFullPath);
Reset();
}
}
#endregion // CreationTimeUtc
#region Exists
///
/// Gets a value indicating whether the file or directory exists.
///
///
/// The property returns if any error occurs while trying to determine if the
/// specified file or directory exists.
/// This can occur in situations that raise exceptions such as passing a directory- or file name with invalid characters or too
/// many characters,
/// a failing or missing disk, or if the caller does not have permission to read the file or directory.
///
/// if the file or directory exists; otherwise, .
public abstract bool Exists { get; }
#endregion // Exists
#region Extension
///
/// Gets the string representing the extension part of the file.
///
///
/// The Extension property returns the extension, including the period (.).
/// For example, for a file c:\NewFile.txt, this property returns ".txt".
///
/// A string containing the extension.
public string Extension
{
get { return Path.GetExtension(FullPath, false); }
}
#endregion // Extension
#region FullName
///
/// Gets the full path of the directory or file.
///
/// A string containing the full path.
public virtual string FullName
{
[SecurityCritical]
get { return FullPath; }
}
#endregion // FullName
#region LastAccessTime
/// Gets or sets the time the current file or directory was last accessed.
///
/// The value of the LastAccessTime property is pre-cached
/// To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions
/// whose values may not be continuously updated by the operating system.
/// If the file described in the FileSystemInfo object does not exist, this property will return
/// 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
///
/// The time that the current file or directory was last accessed.
///
///
public DateTime LastAccessTime
{
[SecurityCritical] get { return LastAccessTimeUtc.ToLocalTime(); }
[SecurityCritical] set { LastAccessTimeUtc = value.ToUniversalTime(); }
}
#endregion // LastAccessTime
#region LastAccessTimeUtc
/// Gets or sets the time, in coordinated universal time (UTC), that the current file or directory was last accessed.
///
/// The value of the LastAccessTimeUtc property is pre-cached.
/// To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions
/// whose values may not be continuously updated by the operating system.
/// If the file described in the FileSystemInfo object does not exist, this property will return
/// 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
///
/// The UTC time that the current file or directory was last accessed.
///
///
[ComVisible(false)]
public DateTime LastAccessTimeUtc
{
[SecurityCritical]
get
{
if (DataInitialised == -1)
{
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
Refresh();
}
// MSDN: .NET 3.5+: IOException: Refresh cannot initialize the data.
if (DataInitialised != 0)
NativeError.ThrowException(DataInitialised, LongFullName);
return DateTime.FromFileTimeUtc(Win32AttributeData.ftLastAccessTime);
}
[SecurityCritical]
set
{
File.SetFsoDateTimeCore(IsDirectory, Transaction, LongFullName, null, value, null, false, PathFormat.LongFullPath);
Reset();
}
}
#endregion // LastAccessTimeUtc
#region LastWriteTime
/// Gets or sets the time when the current file or directory was last written to.
///
/// The value of the LastWriteTime property is pre-cached.
/// To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions
/// whose values may not be continuously updated by the operating system.
/// If the file described in the FileSystemInfo object does not exist, this property will return
/// 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
///
/// The time the current file was last written.
///
///
public DateTime LastWriteTime
{
get { return LastWriteTimeUtc.ToLocalTime(); }
set { LastWriteTimeUtc = value.ToUniversalTime(); }
}
#endregion // LastWriteTime
#region LastWriteTimeUtc
/// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to.
///
/// The value of the LastWriteTimeUtc property is pre-cached. To get the latest value, call the Refresh method.
/// This method may return an inaccurate value, because it uses native functions whose values may not be continuously updated by
/// the operating system.
/// If the file described in the FileSystemInfo object does not exist, this property will return 12:00 midnight, January 1, 1601
/// A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
///
/// The UTC time when the current file was last written to.
[ComVisible(false)]
public DateTime LastWriteTimeUtc
{
[SecurityCritical]
get
{
if (DataInitialised == -1)
{
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
Refresh();
}
// MSDN: .NET 3.5+: IOException: Refresh cannot initialize the data.
if (DataInitialised != 0)
NativeError.ThrowException(DataInitialised, LongFullName);
return DateTime.FromFileTimeUtc(Win32AttributeData.ftLastWriteTime);
}
[SecurityCritical]
set
{
File.SetFsoDateTimeCore(IsDirectory, Transaction, LongFullName, null, null, value, false, PathFormat.LongFullPath);
Reset();
}
}
#endregion // LastWriteTimeUtc
#region Name
///
/// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists.
/// Otherwise, the Name property gets the name of the directory.
///
///
/// For a directory, Name returns only the name of the parent directory, such as Dir, not c:\Dir.
/// For a subdirectory, Name returns only the name of the subdirectory, such as Sub1, not c:\Dir\Sub1.
/// For a file, Name returns only the file name and file name extension, such as MyFile.txt, not c:\Dir\Myfile.txt.
///
///
/// A string that is the name of the parent directory, the name of the last directory in the hierarchy,
/// or the name of a file, including the file name extension.
///
public abstract string Name { get; }
#endregion // Name
#endregion // .NET
#region AlphaFS
#region DisplayPath
/// Returns the path as a string.
protected internal string DisplayPath { get; set; }
#endregion // DisplayPath
#region EntryInfo
private FileSystemEntryInfo _entryInfo;
/// [AlphaFS] Gets the instance of the class.
public FileSystemEntryInfo EntryInfo
{
[SecurityCritical]
get
{
if (_entryInfo == null)
{
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA();
RefreshEntryInfo();
}
// MSDN: .NET 3.5+: IOException: Refresh cannot initialize the data.
if (DataInitialised > 0)
NativeError.ThrowException(DataInitialised, LongFullName);
return _entryInfo;
}
internal set
{
_entryInfo = value;
DataInitialised = value == null ? -1 : 0;
if (DataInitialised == 0)
Win32AttributeData = new NativeMethods.WIN32_FILE_ATTRIBUTE_DATA(_entryInfo.Win32FindData);
}
}
#endregion // EntryInfo
#region IsDirectory
/// [AlphaFS] The initial "IsDirectory" indicator that was passed to the constructor.
protected internal bool IsDirectory { get; set; }
#endregion // IsDirectory
#region LongFullName
/// The full path of the file system object in Unicode (LongPath) format.
protected string LongFullName { get; set; }
#endregion // LongFullName
#region Transaction
/// [AlphaFS] Represents the KernelTransaction that was passed to the constructor.
public KernelTransaction Transaction { get; set; }
#endregion // Transaction
#endregion // AlphaFS
#endregion // Properties
#region Fields
// We use this field in conjunction with the Refresh methods, if we succeed
// we store a zero, on failure we store the HResult in it so that we can
// give back a generic error back.
[NonSerialized] internal int DataInitialised = -1;
// The pre-cached FileSystemInfo information.
[NonSerialized] internal NativeMethods.WIN32_FILE_ATTRIBUTE_DATA Win32AttributeData;
#region .NET
/// Represents the fully qualified path of the file or directory.
///
/// Classes derived from can use the FullPath field
/// to determine the full path of the object being manipulated.
///
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
protected string FullPath;
/// The path originally specified by the user, whether relative or absolute.
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
protected string OriginalPath;
#endregion // .NET
#endregion // Fields
}
}