Как мы получаем доступ к MFT через C#
Мне нужно получить доступ к Windows MFT(Master File Table), используя C# в моем приложении.net.
Я гуглил по этому поводу и не смог найти хороших результатов. Я искал информацию за последние 2 дня, но не смог найти никакой информации о ней.
Я не ищу точный код, чтобы сделать то же самое, я просто ищу информацию, которая поможет мне начать.
Единственное, что мне удалось выяснить, это то, что я должен использовать P/Invoke.
Я хочу знать, какие функции я бы использовал для доступа к MFT.
Если вы можете предоставить пример кода, это было бы здорово.
3 ответа
Во-первых, вы должны иметь и отстаивать достаточные привилегии для доступа к MFT - это боль сама по себе. Затем вы должны получить дескриптор файла / папки на томе - для вызовов на последнем шаге... который должен вызывать Windows API (называемый DeviceIOControl) в цикле и считывать записи из возвращенного вызова API - и это его особая головная боль.
Концептуально - это выглядит так:
static void Main( string[ ] args )
{
if ( Privileges.HasBackupAndRestorePrivileges )
{
using ( var volume = GetVolumeHandle( "C:\\" ) )
{
ReadMft( volume );
}
}
}
Если вы возьмете каждое из них по очереди, утверждение достаточных привилегий является самой неясной частью. Существует Windows API для изменения привилегий работающего токена - и вы используете его для добавления необходимых привилегий. Вот выдержка из класса, который я использую для утверждения этих привилегий. Вы могли бы заявить несколько привилегий - но этого должно быть достаточно для чтения 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 с управляющим кодом ввода-вывода устройства FSCTL_ENUM_USN_DATA, который возвращает одну запись USN для каждой записи в MFT. Для каждого вызова существует множество записей - и после каждого вызова вы параметризуете следующий вызов в цикле с первым битом информации, возвращенным предыдущим вызовом.
Кстати, я переименовал вызовы API Windows в своем коде, чтобы они выглядели более как.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 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;
}
}
А для полноты, вот родные методы, перечисления, константы и шум. Большинство из них из PInvoke.net, но опять же... имена многих из этих вещей были.Net-ified. Извиняюсь перед пуристами.
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 секунды.
Использовать библиотеку https://sourceforge.net/projects/ntfsreader/ от Danny Couture довольно просто:
Меня интересуют два основных вида деятельности. Первая - это чтение информации для данного диска. Вторая часть - обработка полученной информации (поиск заданного файла / папки и т. Д., Переименование всех файлов, поиск файлов длиной больше 260 и т. Д.).
Вся информация возвращается в виде узлов, и это могут быть файлы или папки или другие странные вещи NTFS, такие как расширения и т. Д., Которые мне в настоящее время не нужны.
Вы можете проверить, какие типы узлов вам интересны, протестировавnode.Attributes
- например:
if((node.Attributes & Attributes.Directory) != 0 ) { /*...*/ }
На жестком диске 10 ТБ (не особенно быстро!) С 5 миллионами файлов и 70 тысячами папок извлечение узлов занимает около 60 секунд с использованием минимального режима извлечения - см. Далее Поиск в коллекции Inode(ов) - в моем случае поиск по всем папкам с архивом в названии - меньше 500мс.
Из-за огромной разницы во времени для этих двух действий я считаю, что лучше всего получить все узлы (60 секунд) один раз, а затем выполнить повторный поиск (~500 мс) в коллекции узлов.
Извлечение узла необходимо повторно запустить только в том случае, если папки или файлы изменились.
Вам необходимо добавить ссылку на ntfsreader, и пример его использования показан в следующем псевдокоде:
using System.IO.Filesystem.Ntfs;
public IEnumerable<INode> nodes;
public void DoStuff
{
// Obviously you can also use whatever you like to store this collection; array, dictionary, datatable, etc.
public List<string> foundObjects = new List<string>();
// string drive is "c" or "d" or whatever. Note that there is no "c:\" or "c:".
// Just "c" or "d" for the string drive argument.
// Read the NTFS MFT
RefreshNodes("f");
// If, for example, you are looking for a folder with a given name then
string strPattern="archive";
foreach (INode node in nodes)
{
if ((node.Attributes & Attributes.Directory) != 0 && node.Name.Contains(strPattern))
{
foundObjects.Add(node.FullName.Replace(@"\\", @"\"));
}
}
}
public void RefreshNodes(string drive)
{
DriveInfo driveToAnalyze = new DriveInfo(drive);
NtfsReader ntfsReader = new NtfsReader(driveToAnalyze, RetrieveMode.Minimal);
nodes = ntfsReader.GetNodes(driveToAnalyze.Name);
}
Время, необходимое для извлечения узлов, зависит от используемого RetrieveMode. RetrieveMode.Minimal
возвращает большую часть необходимой мне информации, и это происходит быстрее.
Надеюсь, это поможет, и счастливой NTFSing!
Everything.exe (приложение для поиска на рабочем столе) также имеет тот же доступ, поэтому я надеюсь, что вы сможете найти некоторую информацию в Source-Code of everything.exe.
При первом запуске Everything.exe он создает индекс имен каждого файла и папки на томе из метаданных файла в основной таблице файлов NTFS.[3] По умолчанию все подключенные тома NTFS индексируются.[4] После создания индекс постоянно обновляется приложением из журнала изменений NTFS.[5] Все ищет в этом индексе имена файлов, соответствующие пользовательскому поисковому выражению, которое может быть фрагментом целевого имени файла или регулярным выражением [6], отображая промежуточные результаты при вводе поискового запроса.