Как получить доступ к MFT через C#

Мне нужно получить доступ к Windows MFT (Master File Table) с помощью C# в моем приложении .net.
Я погуглил об этом и не смог найти хороших результатов. Я искал информацию за последние 2 дня, но не смог найти никакой информации о том же самом.

Я не ищу точный код, чтобы сделать то же самое, я просто ищу информацию, которая может заставить меня начал.

единственное, что я смог выяснить, это то, что я должен используйте P / Invoke.
Я хочу знать функции, которые я буду использовать для доступа к MFT.
Если вы можете предоставить пример кода, это было бы здорово.

3 ответов


во - первых, вы должны иметь и утверждать достаточные привилегии для доступа к MFT-это боль сама по себе. Затем вы должны получить дескриптор файла / папки на томе - для вызовов на последнем шаге...который должен вызвать API Windows (называемый DeviceIOControl) в цикле и прочитать записи из возвращенного вызова API - и это его собственная особая головная боль.

концептуально это выглядит так:

static void Main( string[ ] args )
{
  if ( Privileges.HasBackupAndRestorePrivileges )
  {
    using ( var volume = GetVolumeHandle( "C:\" ) )
    {
      ReadMft( volume );
    }
  }
}

если вы берете каждый из них по очереди, утверждая достаточные привилегии - самая темная часть. Существует API Windows для изменения привилегий запущенного токена - и вы используете его для добавления необходимых привилегий. Вот отрывок из класса, который я использую для утверждения этих привилегий. Вы можете утверждать кучу больше привилегий - но этого должно быть достаточно для чтения MFT.

ваше приложение должно будет работать под учетной записью, которая может фактически получить необходимые привилегии-учетная запись администратора хороша. Кроме того, резервная копия оператор будет работать.

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, вы вызываете DeviceIOControl с кодом управления IO устройства FSCTL_ENUM_USN_DATA - который возвращает одну запись USN для каждая запись в MFT. Есть много записей на каждый вызов - и после каждого вызова, вы параметризации следующий вызов в цикле с Первый БИТ инфо вернулось на предыдущий звонок.

BTW - я переименовал вызовы Windows API в своем коде, чтобы они выглядели более похожими на .Net. Я не уверен, что буду делать это в будущем.

особое внимание здесь: вы получаете одну запись для каждого файла независимо от того, сколько жестких ссылок есть - вам придется сделать дополнительные вызовы для перечисления жестких ссылок.

иерархия файловой системы кодируется в 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, но с реализованным интерфейсом сериализации:

[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 закроет поток, который вы обернули вокруг него, - и у меня было много мест, где это было просто невыносимо.

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

и для полноты, вот собственные методы, перечисления, константы и шум. Большинство из них от PInvoke.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
  }

}

вы можете использовать это https://sourceforge.net/projects/ntfsreader/ библиотека с открытым исходным кодом, написанная на C# Дэнни Кутюр.

Я тестировал его и его работы. Он может анализировать диск NTFS с более чем 100000 записей (файл и папка) менее чем за 2 секунды.


все.exe (desktop search application) также получить доступ к тому же так .Я надеюсь, что вы могли бы найти некоторую информацию в Исходный Код все.исполняемый.

Когда Все.exe сначала запускается, он создает индекс имен каждого файла и папки на томе из метаданных файла в таблице NTFS Master File.[3] по умолчанию индексируются все смонтированные Тома NTFS.[4] после создания индекс постоянно обновляется приложением из Журнал изменений NTFS.[5] все ищет этот индекс для имен файлов, соответствующих пользовательскому выражению поиска, которое может быть фрагментом имени целевого файла или регулярным выражением, [6] отображение промежуточных результатов по мере ввода поискового термина.