草庐IT

c# - 我们如何通过C#访问MFT

coder 2024-05-26 原文

我需要在我的 .net 应用程序中使用 C# 访问 Windows MFT(主文件表)。
我已经用谷歌搜索了这个,但找不到任何好的结果。我一直在搜索过去 2 天的信息,但找不到任何相关信息。

我不是在寻找精确的代码来做同样的事情,我只是在寻找一些可以让我开始的信息。

我唯一能弄清楚的是我必须使用 P/Invoke。
我想知道我将用来访问 MFT 的功能。
如果您能够提供一些代码示例,那就太好了。

最佳答案

首先,您必须拥有并声明足够的权限才能访问 MFT - 这本身就是一种痛苦。然后,您必须获得卷上文件/文件夹的句柄 - 对于最后一步中的调用......这是在循环中调用 Windows API(称为 DeviceIOControl)并从返回的 API 调用中读取条目- 这是它自己特别头疼的地方。
从概念上讲 - 这看起来像:

static void Main( string[ ] args )
{
  if ( Privileges.HasBackupAndRestorePrivileges )
  {
    using ( var volume = GetVolumeHandle( "C:\\" ) )
    {
      ReadMft( volume );
    }
  }
}
如果您依次考虑这些中的每一个,则断言足够的特权是最模糊的部分。有一个 Windows API 可以更改正在运行的 token 的权限 - 您可以使用它来添加必要的权限。这是我用来断言这些特权的类的摘录。您可以声明更多特权 - 但这应该足以阅读 MFT。
您的应用程序需要在一个可以实际获得必要权限的帐户下运行 - 管理员帐户很好。此外,备份运算符(operator)将工作。
public static class Privileges
{
  private static int asserted = 0;
  private static bool hasBackupPrivileges = false;

  public static bool HasBackupAndRestorePrivileges
  {
    get { return AssertPriveleges( ); }
  }

  /// <remarks>
  /// First time this method is called, it attempts to set backup privileges for the current process.
  /// Subsequently, it returns the results of that first call.
  /// </remarks>
  private static bool AssertPriveleges( )
  {
    bool success = false;
    var wasAsserted = Interlocked.CompareExchange( ref asserted, 1, 0 );
    if ( wasAsserted == 0 )  // first time here?  come on in!
    {
      success =
        AssertPrivelege( NativeMethods.SE_BACKUP_NAME ) &&
        AssertPrivelege( NativeMethods.SE_RESTORE_NAME );

      hasBackupPrivileges = success;

    }
    return hasBackupPrivileges;
  }


  private static bool AssertPrivelege( string privelege )
  {
    IntPtr token;
    var tokenPrivileges = new NativeMethods.TOKEN_PRIVILEGES( );
    tokenPrivileges.Privileges = new NativeMethods.LUID_AND_ATTRIBUTES[ 1 ];

    var success =
      NativeMethods.OpenProcessToken( NativeMethods.GetCurrentProcess( ), NativeMethods.TOKEN_ADJUST_PRIVILEGES, out token )
      &&
      NativeMethods.LookupPrivilegeValue( null, privelege, out tokenPrivileges.Privileges[ 0 ].Luid );

    try
    {
      if ( success )
      {
        tokenPrivileges.PrivilegeCount = 1;
        tokenPrivileges.Privileges[ 0 ].Attributes = NativeMethods.SE_PRIVILEGE_ENABLED;
        success =
          NativeMethods.AdjustTokenPrivileges( token, false, ref tokenPrivileges, Marshal.SizeOf( tokenPrivileges ), IntPtr.Zero, IntPtr.Zero )
          &&
          ( Marshal.GetLastWin32Error( ) == 0 );
      }

      if ( !success )
      {
        Console.WriteLine( "Could not assert privilege: " + privelege );
      }
    }
    finally
    {
      NativeMethods.CloseHandle( token );
    }

    return success;
  }
}
一旦你通过了那个障碍,剩下的就是——嗯……仍然是一个默默无闻的节日。您必须获得文件或文件夹的句柄 - 具有备份语义。您很可能只需在您所使用的卷上的任何旧文件上打开 FileStream,FileStream 将有一个句柄,您可以将其用于后续调用。这并不完全是我的应用程序所做的 - 但我的应用程序必须做这个答案不需要做的事情。
  internal static SafeFileHandle GetVolumeHandle( string pathToVolume, NativeMethods.EFileAccess access = NativeMethods.EFileAccess.AccessSystemSecurity | NativeMethods.EFileAccess.GenericRead | NativeMethods.EFileAccess.ReadControl )
  {
    var attributes = ( uint ) NativeMethods.EFileAttributes.BackupSemantics;
    var handle = NativeMethods.CreateFile( pathToVolume, access, 7U, IntPtr.Zero, ( uint ) NativeMethods.ECreationDisposition.OpenExisting, attributes, IntPtr.Zero );
    if ( handle.IsInvalid )
    {
      throw new IOException( "Bad path" );
    }

    return handle;
  }
对于 ReadMft - 有一个相当复杂的 Windows API 函数 - DeviceIOControl - 它采用具有史诗般的各种输入的缓冲区并返回包含令人费解的各种输出的缓冲区。它是一种用于查询各种设备信息的包罗万象的 API - 包含 MFT 的卷是一个设备。
要读取 MFT,您可以使用 FSCTL_ENUM_USN_DATA 的设备 IO 控制代码调用 DeviceIOControl - 它为 MFT 中的每个记录返回一个 USN 记录。每次调用都有很多记录 - 在每次调用之后,您使用前一个调用返回的第一位信息参数化循环中的下一个调用。
顺便说一句 - 我在我的代码中重命名了 Windows API 调用,使它们看起来更像 .Net。我不确定我将来会不会这样做。
此处特别说明 :您会为每个文件获得一条记录 - 无论有多少硬链接(hard link) - 您必须执行额外的调用来枚举硬链接(hard link)。
文件系统层次结构在您从调用中返回的结构的 FileReferenceNumber 和 ParentFileReferenceNumber 中进行编码。您名义上会将这些 usn 记录保存到一个列表中,按 FileReferenceNumber 排序并为 ParentFileReferenceNumber 创建二级索引 - 或类似的东西。出于说明的目的,此代码仅转储 MFT 条目。
此示例使用不安全代码 - 并修复了包含输入和输出的缓冲区的位置。有不同的方法来解决这个问题 - 但这是很好的和活泼的。如果你使用它,你必须在你的项目设置中允许不安全的代码。
public unsafe static bool ReadMft( SafeHandle volume )
{
  var outputBufferSize = 1024 * 1024;
  var input = new NativeMethods.MFTEnumDataV0( );
  var usnRecord = new NativeMethods.UsnRecordV2( );

  var outputBuffer = new byte[ outputBufferSize ];

  var okay = true;
  var doneReading = false;

  try
  {
    fixed ( byte* pOutput = outputBuffer )
    {
      input.StartFileReferenceNumber = 0;
      input.LowUsn = 0;
      input.HighUsn = long.MaxValue;

      using ( var stream = new MemoryStream( outputBuffer, true ) )
      {
        while ( !doneReading )
        {
          var bytesRead = 0U;
          okay = NativeMethods.DeviceIoControl
          (
            volume.DangerousGetHandle( ),
            NativeMethods.DeviceIOControlCode.FsctlEnumUsnData,
            ( byte* ) &input.StartFileReferenceNumber,
            ( uint ) Marshal.SizeOf( input ),
            pOutput,
            ( uint ) outputBufferSize,
            out bytesRead,
            IntPtr.Zero
          );

          if ( !okay )
          {
            var error = Marshal.GetLastWin32Error( );
            okay = error == NativeMethods.ERROR_HANDLE_EOF;
            if ( !okay )
            {
              Console.WriteLine( "Crap! Windows error " + error.ToString( ) );
              break;
            }
            else
            {
              doneReading = true;
            }
          }

          input.StartFileReferenceNumber = stream.ReadULong( );
          while ( stream.Position < bytesRead )
          {
            usnRecord.Read( stream );

            //-->>>>>>>>>>>>>>>>> 
            //--> just an example of reading out the record...
            Console.WriteLine( "FRN:" + usnRecord.FileReferenceNumber.ToString( ) );
            Console.WriteLine( "Parent FRN:" + usnRecord.ParentFileReferenceNumber.ToString( ) );
            Console.WriteLine( "File name:" + usnRecord.FileName );
            Console.WriteLine( "Attributes: " + ( NativeMethods.EFileAttributes ) usnRecord.FileAttributes );
            Console.WriteLine( "Timestamp:" + usnRecord.TimeStamp );
            //-->>>>>>>>>>>>>>>>>>> 
          }
          stream.Seek( 0, SeekOrigin.Begin );
        }
      }
    }
  }
  catch ( Exception ex )
  {
    Console.Write( ex );
    okay = false;
  }
  return okay;
}
我做了一些可能有点俗气的事情来节省自己的大量工作——我向 Windows API 结构添加了伪序列化方法——这样它们就可以从流中读取自己。比如上面代码中用于读取缓冲区的usnRecord就是一个windows API结构——但是实现了一个序列化接口(interface):
[StructLayout( LayoutKind.Sequential )]
internal struct UsnRecordV2: IBinarySerialize
{
  public uint RecordLength;
  public ushort MajorVersion;
  public ushort MinorVersion;
  public ulong FileReferenceNumber;
  public ulong ParentFileReferenceNumber;
  public long Usn;
  public long TimeStamp;
  public UsnReason Reason;
  public uint SourceInfo;
  public uint SecurityId;
  public uint FileAttributes;
  public ushort FileNameLength;
  public ushort FileNameOffset;
  public string FileName;

  /// <remarks>
  /// Note how the read advances to the FileNameOffset and reads only FileNameLength bytes.
  /// </remarks>
  public void Read( Stream stream )
  {
    var startOfRecord = stream.Position;
    RecordLength = stream.ReadUInt( );
    MajorVersion = stream.ReadUShort( );
    MinorVersion = stream.ReadUShort( );
    FileReferenceNumber = stream.ReadULong( );
    ParentFileReferenceNumber = stream.ReadULong( );
    Usn = stream.ReadLong( );
    TimeStamp = stream.ReadLong( );
    Reason = ( UsnReason ) stream.ReadUInt( );
    SourceInfo = stream.ReadUInt( );
    SecurityId = stream.ReadUInt( );
    FileAttributes = stream.ReadUInt( );
    FileNameLength = stream.ReadUShort( );
    FileNameOffset = stream.ReadUShort( );
    stream.Position = startOfRecord + FileNameOffset;
    FileName = Encoding.Unicode.GetString( stream.ReadBytes( FileNameLength ) );
    stream.Position = startOfRecord + RecordLength;

  }

  /// <summary>We never write instances of this structure</summary>
  void IBinarySerialize.Write( Stream stream )
  {
    throw new NotImplementedException( );
  }
}
...其中 IBinarySerialze 是:
public interface IBinarySerialize
{
  /// <summary>Reads an object's data from a <see cref="Stream"/></summary>
  void Read( Stream stream );

  /// <summary>Writes an objects serializable content to a <see cref="Stream"/></summary>
  void Write( Stream stream );

}
此结构中使用了流扩展方法。基本上,它们是从 BinaryReader 中提取的。为什么?因为在 .Net 3.5 中——我最初必须写这个——BCL BinaryReader 会关闭你包裹它的流——我有很多地方是不能容忍的。
internal static class StreamingExtensions
{
  public static ushort ReadUShort( this Stream stream )
  {
    return BitConverter.ToUInt16( ReadBytes( stream, 2 ), 0 );
  }

  public static uint ReadUInt( this Stream stream )
  {
    return BitConverter.ToUInt32( ReadBytes( stream, 4 ), 0 );
  }

  public static long ReadLong( this Stream stream )
  {
    return BitConverter.ToInt64( ReadBytes( stream, 8 ), 0 );
  }

  public static ulong ReadULong( this Stream stream )
  {
    return BitConverter.ToUInt64( ReadBytes( stream, 8 ), 0 );
  }
  public static byte[ ] ReadBytes( this Stream stream, int length, bool throwIfIncomplete = false )
  {
    var bytes = new byte[ length ];
    var bytesRead = 0;
    var offset = 0;
    if ( length > 0 )
    {
      while ( offset < length )
      {
        bytesRead = stream.Read( bytes, offset, length - offset );
        if ( bytesRead == 0 )
        {
          if ( throwIfIncomplete ) throw new InvalidOperationException( "incomplete" );
          break;
        }
        offset += bytesRead;
      }
    }
    return bytes;
  }
}
为了完整起见,这里是 native 方法、枚举、常量和噪声。大多数来自 PInvoke.net,但同样......许多这些东西的名称是 .Net 化的。向纯粹主义者道歉。
internal class NativeMethods
{
  internal const int ERROR_HANDLE_EOF = 38;

  //--> Privilege constants....
  internal const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
  internal const string SE_BACKUP_NAME = "SeBackupPrivilege";
  internal const string SE_RESTORE_NAME = "SeRestorePrivilege";
  internal const string SE_SECURITY_NAME = "SeSecurityPrivilege";
  internal const string SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege";
  internal const string SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege";
  internal const string SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege";
  internal const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
  internal const string SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege";
  internal const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege";
  internal const string SE_TCB_NAME = "SeTcbPrivilege";
  internal const string SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege";
  internal const string SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege";

  //--> For starting a process in session 1 from session 0...
  internal const int TOKEN_DUPLICATE = 0x0002;
  internal const uint MAXIMUM_ALLOWED = 0x2000000;
  internal const int CREATE_NEW_CONSOLE = 0x00000010;
  internal const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
  internal const int TOKEN_QUERY = 0x00000008;


  [DllImport( "advapi32.dll", SetLastError = true )]
  [return: MarshalAs( UnmanagedType.Bool )]
  internal static extern bool OpenProcessToken( IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle );
  [DllImport( "kernel32.dll" )]
  internal static extern IntPtr GetCurrentProcess( );
  [DllImport( "advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
  [return: MarshalAs( UnmanagedType.Bool )]
  internal static extern bool LookupPrivilegeValue( string lpSystemName, string lpName, out LUID lpLuid );
  [DllImport( "advapi32.dll", SetLastError = true )]
  [return: MarshalAs( UnmanagedType.Bool )]
  internal static extern bool AdjustTokenPrivileges( IntPtr TokenHandle, [MarshalAs( UnmanagedType.Bool )]bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, Int32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength );
  [DllImport( "kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode )]
  [return: MarshalAs( UnmanagedType.Bool )]
  internal static unsafe extern bool DeviceIoControl( IntPtr hDevice, DeviceIOControlCode controlCode, byte* lpInBuffer, uint nInBufferSize, byte* lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped );
  [DllImport( "kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode )]
  internal static extern SafeFileHandle CreateFile( string lpFileName, EFileAccess dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile );
  [DllImport( "kernel32.dll", SetLastError = true )]
  [return: MarshalAs( UnmanagedType.Bool )]
  internal static extern bool CloseHandle( IntPtr hObject );


  [Flags]
  internal enum EMethod: uint
  {
    Buffered = 0,
    InDirect = 1,
    OutDirect = 2,
    Neither = 3
  }

  [Flags]
  internal enum EFileAccess: uint
  {
    GenericRead = 0x80000000,
    GenericWrite = 0x40000000,
    GenericExecute = 0x20000000,
    GenericAll = 0x10000000,

    Delete = 0x10000,
    ReadControl = 0x20000,
    WriteDAC = 0x40000,
    WriteOwner = 0x80000,
    Synchronize = 0x100000,

    StandardRightsRequired = 0xF0000,
    StandardRightsRead = ReadControl,
    StandardRightsWrite = ReadControl,
    StandardRightsExecute = ReadControl,
    StandardRightsAll = 0x1F0000,
    SpecificRightsAll = 0xFFFF,

    AccessSystemSecurity = 0x1000000,
    MaximumAllowed = 0x2000000
  }


  [Flags]
  internal enum EFileDevice: uint
  {
    Beep = 0x00000001,
    CDRom = 0x00000002,
    CDRomFileSytem = 0x00000003,
    Controller = 0x00000004,
    Datalink = 0x00000005,
    Dfs = 0x00000006,
    Disk = 0x00000007,
    DiskFileSystem = 0x00000008,
    FileSystem = 0x00000009,
    InPortPort = 0x0000000a,
    Keyboard = 0x0000000b,
    Mailslot = 0x0000000c,
    MidiIn = 0x0000000d,
    MidiOut = 0x0000000e,
    Mouse = 0x0000000f,
    MultiUncProvider = 0x00000010,
    NamedPipe = 0x00000011,
    Network = 0x00000012,
    NetworkBrowser = 0x00000013,
    NetworkFileSystem = 0x00000014,
    Null = 0x00000015,
    ParallelPort = 0x00000016,
    PhysicalNetcard = 0x00000017,
    Printer = 0x00000018,
    Scanner = 0x00000019,
    SerialMousePort = 0x0000001a,
    SerialPort = 0x0000001b,
    Screen = 0x0000001c,
    Sound = 0x0000001d,
    Streams = 0x0000001e,
    Tape = 0x0000001f,
    TapeFileSystem = 0x00000020,
    Transport = 0x00000021,
    Unknown = 0x00000022,
    Video = 0x00000023,
    VirtualDisk = 0x00000024,
    WaveIn = 0x00000025,
    WaveOut = 0x00000026,
    Port8042 = 0x00000027,
    NetworkRedirector = 0x00000028,
    Battery = 0x00000029,
    BusExtender = 0x0000002a,
    Modem = 0x0000002b,
    Vdm = 0x0000002c,
    MassStorage = 0x0000002d,
    Smb = 0x0000002e,
    Ks = 0x0000002f,
    Changer = 0x00000030,
    Smartcard = 0x00000031,
    Acpi = 0x00000032,
    Dvd = 0x00000033,
    FullscreenVideo = 0x00000034,
    DfsFileSystem = 0x00000035,
    DfsVolume = 0x00000036,
    Serenum = 0x00000037,
    Termsrv = 0x00000038,
    Ksec = 0x00000039,
    // From Windows Driver Kit 7
    Fips = 0x0000003A,
    Infiniband = 0x0000003B,
    Vmbus = 0x0000003E,
    CryptProvider = 0x0000003F,
    Wpd = 0x00000040,
    Bluetooth = 0x00000041,
    MtComposite = 0x00000042,
    MtTransport = 0x00000043,
    Biometric = 0x00000044,
    Pmi = 0x00000045
  }

  internal enum EFileIOCtlAccess: uint
  {
    Any = 0,
    Special = Any,
    Read = 1,
    Write = 2
  }

  internal enum DeviceIOControlCode: uint
  {
    FsctlEnumUsnData = ( EFileDevice.FileSystem << 16 ) | ( 44 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
    FsctlReadUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 46 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
    FsctlReadFileUsnData = ( EFileDevice.FileSystem << 16 ) | ( 58 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 ),
    FsctlQueryUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 61 << 2 ) | EMethod.Buffered | ( EFileIOCtlAccess.Any << 14 ),
    FsctlCreateUsnJournal = ( EFileDevice.FileSystem << 16 ) | ( 57 << 2 ) | EMethod.Neither | ( EFileIOCtlAccess.Any << 14 )
  }

  /// <summary>Control structure used to interrogate MFT data using DeviceIOControl from the user volume</summary>
  [StructLayout( LayoutKind.Sequential )]
  internal struct MFTEnumDataV0
  {
    public ulong StartFileReferenceNumber;
    public long LowUsn;
    public long HighUsn;
  }


  /// <summary>A structure resurned form USN queries</summary>
  /// <remarks>
  /// FileName is synthetic...composed during a read of the structure and is not technically
  /// part of the Win32 API's definition...although the actual FileName is contained
  /// "somewhere" in the structure's trailing bytes, according to FileNameLength and FileNameOffset.
  /// 
  /// Alignment boundaries are enforced, and so, the RecordLength
  /// may be somewhat larger than the accumulated lengths of the members plus the FileNameLength.
  /// </remarks>
  [StructLayout( LayoutKind.Sequential )]
  internal struct UsnRecordV2: IBinarySerialize
  {
    public uint RecordLength;
    public ushort MajorVersion;
    public ushort MinorVersion;
    public ulong FileReferenceNumber;
    public ulong ParentFileReferenceNumber;
    public long Usn;
    public long TimeStamp;
    public UsnReason Reason;
    public uint SourceInfo;
    public uint SecurityId;
    public uint FileAttributes;
    public ushort FileNameLength;
    public ushort FileNameOffset;
    public string FileName;

    /// <remarks>Note how the read advances to the FileNameOffset and reads only FileNameLength bytes</remarks>
    public void Read( Stream stream )
    {
      var startOfRecord = stream.Position;
      RecordLength = stream.ReadUInt( );
      MajorVersion = stream.ReadUShort( );
      MinorVersion = stream.ReadUShort( );
      FileReferenceNumber = stream.ReadULong( );
      ParentFileReferenceNumber = stream.ReadULong( );
      Usn = stream.ReadLong( );
      TimeStamp = stream.ReadLong( );
      Reason = ( UsnReason ) stream.ReadUInt( );
      SourceInfo = stream.ReadUInt( );
      SecurityId = stream.ReadUInt( );
      FileAttributes = stream.ReadUInt( );
      FileNameLength = stream.ReadUShort( );
      FileNameOffset = stream.ReadUShort( );
      stream.Position = startOfRecord + FileNameOffset;
      FileName = Encoding.Unicode.GetString( stream.ReadBytes( FileNameLength ) );
      stream.Position = startOfRecord + RecordLength;

    }

    void IBinarySerialize.Write( Stream stream )
    {
      throw new NotImplementedException( );
    }
  }

  /// <summary>Structure returned from USN query that describes the state of the journal</summary>
  [StructLayout( LayoutKind.Sequential )]
  internal struct UsnJournalDataV1: IBinarySerialize
  {
    public ulong UsnJournalId;
    public long FirstUsn;
    public long NextUsn;
    public long LowestValidUsn;
    public long MaxUsn;
    public ulong MaximumSize;
    public ulong AllocationDelta;
    public ushort MinSupportedMajorVersion;
    public ushort MaxSupportedMajorVersion;

    public void Read( Stream stream )
    {
      UsnJournalId = stream.ReadULong( );
      FirstUsn = stream.ReadLong( );
      NextUsn = stream.ReadLong( );
      LowestValidUsn = stream.ReadLong( );
      MaxUsn = stream.ReadLong( );
      MaximumSize = stream.ReadULong( );
      AllocationDelta = stream.ReadULong( );
      MinSupportedMajorVersion = stream.ReadUShort( );
      MaxSupportedMajorVersion = stream.ReadUShort( );
    }

    void IBinarySerialize.Write( Stream stream )
    {
      throw new NotImplementedException( );
    }
  }

  [StructLayout( LayoutKind.Sequential )]
  internal struct LUID
  {
    public UInt32 LowPart;
    public Int32 HighPart;
  }


  [StructLayout( LayoutKind.Sequential )]
  internal struct LUID_AND_ATTRIBUTES
  {
    public LUID Luid;
    public UInt32 Attributes;
  }


  internal struct TOKEN_PRIVILEGES
  {
    public UInt32 PrivilegeCount;
    [MarshalAs( UnmanagedType.ByValArray, SizeConst = 1 )]      // !! think we only need one
    public LUID_AND_ATTRIBUTES[ ] Privileges;
  }

  [Flags]
  internal enum EFileAttributes: uint
  {
    /// <summary/>
    None = 0,

    //-->  these are consistent w/ .Net FileAttributes...
    Readonly = 0x00000001,
    Hidden = 0x00000002,
    System = 0x00000004,
    Directory = 0x00000010,
    Archive = 0x00000020,
    Device = 0x00000040,
    Normal = 0x00000080,
    Temporary = 0x00000100,
    SparseFile = 0x00000200,
    ReparsePoint = 0x00000400,
    Compressed = 0x00000800,
    Offline = 0x00001000,
    NotContentIndexed = 0x00002000,
    Encrypted = 0x00004000,

    //--> additional CreateFile call attributes...
    Write_Through = 0x80000000,
    Overlapped = 0x40000000,
    NoBuffering = 0x20000000,
    RandomAccess = 0x10000000,
    SequentialScan = 0x08000000,
    DeleteOnClose = 0x04000000,
    BackupSemantics = 0x02000000,
    PosixSemantics = 0x01000000,
    OpenReparsePoint = 0x00200000,
    OpenNoRecall = 0x00100000,
    FirstPipeInstance = 0x00080000
  }

  /// <summary>Reasons the file changed (from USN journal)</summary>
  [Flags]
  public enum UsnReason: uint
  {
    BASIC_INFO_CHANGE = 0x00008000,
    CLOSE = 0x80000000,
    COMPRESSION_CHANGE = 0x00020000,
    DATA_EXTEND = 0x00000002,
    DATA_OVERWRITE = 0x00000001,
    DATA_TRUNCATION = 0x00000004,
    EA_CHANGE = 0x00000400,
    ENCRYPTION_CHANGE = 0x00040000,
    FILE_CREATE = 0x00000100,
    FILE_DELETE = 0x00000200,
    HARD_LINK_CHANGE = 0x00010000,
    INDEXABLE_CHANGE = 0x00004000,
    NAMED_DATA_EXTEND = 0x00000020,
    NAMED_DATA_OVERWRITE = 0x00000010,
    NAMED_DATA_TRUNCATION = 0x00000040,
    OBJECT_ID_CHANGE = 0x00080000,
    RENAME_NEW_NAME = 0x00002000,
    RENAME_OLD_NAME = 0x00001000,
    REPARSE_POINT_CHANGE = 0x00100000,
    SECURITY_CHANGE = 0x00000800,
    STREAM_CHANGE = 0x00200000,
  
    None = 0x00000000
  }

  internal enum ECreationDisposition: uint
  {
    New = 1,
    CreateAlways = 2,
    OpenExisting = 3,
    OpenAlways = 4,
    TruncateExisting = 5
  }

}

关于c# - 我们如何通过C#访问MFT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21661798/

有关c# - 我们如何通过C#访问MFT的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

随机推荐