简体   繁体   English

USN_RECORD的所有值都可以,但TimeStamp = 0除外?

[英]USN_RECORD all values ok, except TimeStamp = 0?

Using below record structure and function I get the FileName,Reference Numbers,Versions etc. at high speed. 使用下面的记录结构和功能,可以高速获取文件名,参考编号,版本等。 (trying with Delphi, Win 7) (尝试使用Delphi,Win 7)

Problem is that I don't get the file DateTime (TimeStamp.QuadPart = 0) 问题是我没有得到文件DateTime(TimeStamp.QuadPart = 0)

I have added the working code. 我已经添加了工作代码。 For testing I am adding the filename (shows correct) and timestamp (shows empty) to memo1 为了进行测试,我将文件名(显示正确)和时间戳(显示空)添加到memo1

procedure Tform_main.Button1Click(Sender: TObject);
begin
   FillFileListFromUSNJournal('C') ;
end;


procedure Tform_main.FillFileListFromUSNJournal(pDrive:Char);
var
  ARootHandle: Cardinal;
  AMFTEnumBuff: Pointer;
begin
    ARootHandle := GetRootHandle(pDrive);
    if AllocMFTEnumBuffer(ARootHandle,AMFTEnumBuff) then
      EnumMFTEntries(ARootHandle, AMFTEnumBuff, MFTEnumCallback, @pDrive) ;
end;

function Tform_main.MFTEnumCallback(AUSN: PUSNRecord; Extra: Pointer): Boolean;
var
  AName,ProgressMsg: String;
  Drive: Char;
begin
  Drive  := PChar(Extra)^;
  Result := True;
  USNRecFromPointer(AUSN) ;
end;




{
  ** uMFT.pas
  ** zm
  ** created: 13.11.2010
  **
  *  Copyright (c) 2010, Zeljko Marjanovic <xxxxxxr@gmail.com>
  *  This code is licensed under MPL 1.1
  *  For details, see http://www.mozilla.org/MPL/MPL-1.1.html
  *
}

unit uMFT;

interface

uses
  Windows, SysUtils;

const
  FILE_DEVICE_FILE_SYSTEM = $00000009;
  METHOD_NEITHER = 3;
  METHOD_BUFFERED = 0;
  FILE_ANY_ACCESS = 0;
  FILE_SPECIAL_ACCESS = 0;
  FILE_READ_ACCESS = 1;
  FILE_WRITE_ACCESS = 2;

  ERROR_JOURNAL_DELETE_IN_PROGRESS = 1178;
  ERROR_JOURNAL_NOT_ACTIVE  = 1179;
  ERROR_JOURNAL_ENTRY_DELETED = 1181;

  FSCTL_GET_OBJECT_ID = $9009c;
  FSCTL_ENUM_USN_DATA = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (44 shl 2) or METHOD_NEITHER;
  FSCTL_READ_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (46 shl 2) or METHOD_NEITHER;
  FSCTL_CREATE_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (57 shl 2) or METHOD_NEITHER;
  FSCTL_QUERY_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (61 shl 2) or METHOD_BUFFERED;
  FSCTL_DELETE_USN_JOURNAL = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (62 shl 2) or METHOD_BUFFERED;

  USN_PAGE_SIZE = $1000;
  USN_REASON_DATA_OVERWRITE = $00000001;
  USN_REASON_DATA_EXTEND = $00000002;
  USN_REASON_DATA_TRUNCATION = $00000004;
  USN_REASON_NAMED_DATA_OVERWRITE = $00000010;
  USN_REASON_NAMED_DATA_EXTEND = $00000020;
  USN_REASON_NAMED_DATA_TRUNCATION = $00000040;
  USN_REASON_FILE_CREATE = $00000100;
  USN_REASON_FILE_DELETE = $00000200;
  USN_REASON_EA_CHANGE = $00000400;
  USN_REASON_SECURITY_CHANGE = $00000800;
  USN_REASON_RENAME_OLD_NAME = $00001000;
  USN_REASON_RENAME_NEW_NAME = $00002000;
  USN_REASON_INDEXABLE_CHANGE = $00004000;
  USN_REASON_BASIC_INFO_CHANGE = $00008000;
  USN_REASON_HARD_LINK_CHANGE = $00010000;
  USN_REASON_COMPRESSION_CHANGE = $00020000;
  USN_REASON_ENCRYPTION_CHANGE = $00040000;
  USN_REASON_OBJECT_ID_CHANGE = $00080000;
  USN_REASON_REPARSE_POINT_CHANGE = $00100000;
  USN_REASON_STREAM_CHANGE = $00200000;
  USN_REASON_CLOSE = $80000000;

  USN_DELETE_FLAG_DELETE = $00000001;
  USN_DELETE_FLAG_NOTIFY = $00000002;
  USN_DELETE_VALID_FLAGS = $00000003;

  USNREC_MAJVER_OFFSET = 4;
  USNREC_MINVER_OFFSET = 8; 
  USNREC_FR_OFFSET = 8;
  USNREC_PFR_OFFSET = 16;
  USNREC_USN_OFFSET = 24;
  USNREC_TIMESTAMP_OFFSET = 32;
  USNREC_REASON_OFFSET = 40;
  USNREC_SINFO_OFFSET = 44;
  USNREC_SECID_OFFSET = 48;
  USNREC_FA_OFFSET = 52;
  USNREC_FNL_OFFSET = 56;
  USNREC_FN_OFFSET = 58;

  IOCTL_DISK_BASE = $00000007;
  IOCTL_DISK_GET_PARTITION_INFO = (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0001 shl 2) or METHOD_BUFFERED;
  PARTITION_IFS = $07; 

type
  USN_JOURNAL_DATA = record
    UsnJournalID: UInt64;
    FirstUsn: Int64;
    NextUsn: Int64;
    LowestValidUsn: Int64;
    MaxUsn: Int64;
    MaximumSize: UInt64;
    AllocationDelta: UInt64;
  end;
  TUSNJournalData = USN_JOURNAL_DATA;
  PUSNJournalData = ^TUSNJournalData;

  MFT_ENUM_DATA = record
    StartFileReferenceNumber: UInt64;
    LowUsn: Int64;
    HighUsn: Int64;
  end;
  TMFTEnumData = MFT_ENUM_DATA;
  PMFTEnumData = ^TMFTEnumData;

  CREATE_USN_JOURNAL_DATA = record
    MaximumSize: UInt64;
    AllocationDelta: UInt64;
  end;
  TCreateUSNJournalData = CREATE_USN_JOURNAL_DATA;
  PCreateUSNJournalData = ^TCreateUSNJournalData;

  USN_RECORD = record
    RecordLength: Cardinal;
    MajorVersion: Word;
    MinorVersion: Word;
    FileReferenceNumber: UInt64;
    ParentFileReferenceNumber: UInt64;
    Usn: Int64;
    TimeStamp: LARGE_INTEGER;
    Reason: Cardinal;
    SourceInfo: Cardinal;
    SecurityId: Cardinal;
    FileAttributes: Cardinal;
    FileNameLength: Word;
    FileNameOffset: Word;
    FileName: PWideChar;//   PWChar; [ss]
  end;
  TUSNRecord = USN_RECORD;
  PUSNRecord = ^TUSNRecord;

  TMFTEnumCallback = function(AUSN: PUSNRecord; Extra: Pointer = nil): Boolean of object;
  PUInt64 = ^UInt64;
  EMFTException = class(Exception);

  TUSNRecChangeType = (uceNew, uceDeleted, uceRenamed);


function USNRecFromPointer(const P: Pointer): TUSNRecord;
function GetRootHandle(const Drive: Char): Cardinal;
function CreateUSNJournal(ARootHandle: Cardinal; MaxSize, AllocationDelta: UInt64): Boolean;
function AllocMFTEnumBuffer(ARootHandle: Cardinal; var AMFTEnumBuff: Pointer): boolean;
function EnumMFTEntries(ARootHandle: Cardinal; AMFTEnumBuff: Pointer; EnumCallBack: TMFTEnumCallback;Extra: Pointer = nil): Boolean;

implementation
uses main ;

function USNRecFromPointer(const P: Pointer): TUSNRecord;
var
  PA: PAnsiChar;
begin
  PA := PAnsiChar(P);
  Result.RecordLength := PInteger(PA)^ ;
  Result.MajorVersion := PInteger(PA + USNREC_MAJVER_OFFSET)^;
  Result.MinorVersion := PInteger(PA + USNREC_MINVER_OFFSET)^;
  Result.FileReferenceNumber := PUInt64(PA + USNREC_FR_OFFSET)^;
  Result.ParentFileReferenceNumber := PUInt64(PA + USNREC_PFR_OFFSET)^;
  Result.USN := PInt64(PA + USNREC_USN_OFFSET)^;
  Result.TimeStamp.QuadPart := PInt64(PA + USNREC_TIMESTAMP_OFFSET)^;
  Result.Reason := PCardinal(PA + USNREC_REASON_OFFSET)^;
  Result.SourceInfo := PCardinal(PA + USNREC_SINFO_OFFSET)^;
  Result.SecurityId := PCardinal(PA + USNREc_SECID_OFFSET)^;
  Result.FileAttributes := PCardinal(PA + USNREC_FA_OFFSET)^;
  Result.FileNameLength := PWord(PA + USNREC_FNL_OFFSET)^;
  Result.FileNameOffset := PWord(PA + USNREC_FN_OFFSET)^;
  Result.FileName :=   PWideChar(Integer(P) + Result.FileNameOffset);
  if form_Main.memo1.lines.Count<100 then
    form_Main.memo1.lines.add(Result.FileName+' '+InttoStr(Result.TimeStamp.QuadPart)) ;

end;


// this requires admin privileges
function GetRootHandle(const Drive: Char): Cardinal;
var
  W: WideString;
  RootHandle: Cardinal;
  Flags: Cardinal;
  Access: Cardinal;
begin
  Flags := 0;
  W := '\\.\' + Drive + ':';
  Access := GENERIC_READ;
  RootHandle := CreateFileW(PWChar(W), Access, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, Flags, 0);
  if RootHandle <> INVALID_HANDLE_VALUE then
    Result := RootHandle
  else
    raise EMFTException.Create(SysErrorMessage(GetLastError));
end;

function CreateUSNJournal(ARootHandle: Cardinal; MaxSize, AllocationDelta: UInt64): Boolean;
var
  BytesRet: Cardinal;
  CreateData: TCreateUSNJournalData;
begin
  CreateData.MaximumSize := MaxSize;
  CreateData.AllocationDelta := AllocationDelta;
  Result := DeviceIoControl(ARootHandle, FSCTL_CREATE_USN_JOURNAL, @CreateData, sizeof(TCreateUSNJournalData), nil, 0, BytesRet, nil);
end;


function AllocMFTEnumBuffer(ARootHandle: Cardinal; var AMFTEnumBuff: Pointer): boolean;
var
  USNBuf: TUSNJournalData;
  ErrCode,BytesRet: Cardinal;
  EnumBuf: PMFTEnumData;
begin
  result:=false;
  if DeviceIoControl(ARootHandle, FSCTL_QUERY_USN_JOURNAL, nil, 0, @USNBuf, sizeof(TUSNJournalData), BytesRet, nil) then
  begin
    GetMem(EnumBuf, sizeof(TMFTEnumData));
    EnumBuf.StartFileReferenceNumber := 0;
    EnumBuf.LowUsn := 0;
    EnumBuf.HighUsn := USNBuf.NextUsn;
    AMFTEnumBuff := EnumBuf;
    result:=true;
    exit;
  end;

  ErrCode:=GetLastError;
  if ErrCode = ERROR_JOURNAL_NOT_ACTIVE then
  begin
    // journal does not exist, create a new1 one
    if CreateUSNJournal(ARootHandle, $10000000, $100000) then
    begin
      if DeviceIoControl(ARootHandle, FSCTL_QUERY_USN_JOURNAL, nil, 0, @USNBuf, sizeof(TUSNJournalData), BytesRet, nil) then
      begin
        GetMem(EnumBuf, sizeof(TMFTEnumData));
        EnumBuf.StartFileReferenceNumber := 0;
        EnumBuf.LowUsn := 0;
        EnumBuf.HighUsn := USNBuf.NextUsn;
        AMFTEnumBuff := EnumBuf;
        result:=true;
        exit;
      end;
      ErrCode:=GetLastError;
    end
    else
    begin
      ErrCode:=GetLastError;
    end;
  end;
end;

//v main function
function EnumMFTEntries(ARootHandle: Cardinal; AMFTEnumBuff: Pointer; EnumCallBack: TMFTEnumCallback;Extra: Pointer): Boolean;
const
  BUF_SIZE = sizeof(UInt64) + $10000;
var
  P: Pointer;
  MFTEnum: Pointer;
  BytesRet: Cardinal;
  PUSN: PUSNRecord;
//  TUSN: TUSNRecord;
begin
  Result := False;
  if (ARootHandle = INVALID_HANDLE_VALUE) or (AMFTEnumBuff = nil) then
    Exit;

  MFTEnum := AMFTEnumBuff;
  GetMem(P, BUF_SIZE);
  try
    ZeroMemory(P, BUF_SIZE);
    while DeviceIoControl(ARootHandle, FSCTL_ENUM_USN_DATA, MFTEnum, sizeof(TMFTEnumData), P, BUF_SIZE, BytesRet, nil) do
    begin
      PUSN := PUSNRecord(Integer(P) + sizeof(Int64));

      while BytesRet > 60 do
      begin
        if (not EnumCallBack(PUSN, Extra)) then
          Exit;
        if PUSN.RecordLength > 0 then
          Dec(BytesRet, PUSN.RecordLength)
        else
          break;
        PUSN := PUSNRecord(Cardinal(PUSN) + PUSN.RecordLength);
        if form_Main.memo1.lines.Count>100 then
          break ;
      end;
      CopyMemory(MFTEnum, P, sizeof(Int64));
    end;
    Result := True;

  finally
    FreeMem(P);
  end;
end;
//^ main function


end.

Time stamps come up 0 because FSCTL_QUERY_USN_JOURNAL does not populate that information. 时间戳记为0,因为FSCTL_QUERY_USN_JOURNAL不填充该信息。 This is not explicitly stated in the control code's documentation and I cannot find any official documentation where it is. 在控制代码的文档中没有明确说明,因此我找不到任何官方文档。 But there are places that mention a USN_RECORD is only partially populated. 但是有些地方提到USN_RECORD仅部分填充。 Like in this link which is given from USN_RECORD_V2 's documentation , although I'm not even sure what FSCTL the former link is talking about. 就像从USN_RECORD_V2文档中给出的此链接一样,尽管我甚至不确定前一个链接在谈论什么FSCTL。

Anyway, this is inline with all the examples I can find including Microsoft's own . 无论如何,这与我可以找到的所有示例(包括Microsoft 自己的示例)一致。 Below is a Delphi translation of this code. 下面是此代码的Delphi转换。 The USN information retrieved by using a FSCTL_QUERY_USN_JOURNAL control code is used in a DeviceIoControl call using FSCTL_READ_USN_JOURNAL to retrieve detailed information. 通过使用检索到的信息USN FSCTL_QUERY_USN_JOURNAL控制代码是在使用DeviceIoControl呼叫使用FSCTL_READ_USN_JOURNAL检索的详细信息。

There are few exceptions of a direct translation, one is the retrieval of the file name, which I didn't understand how the C++ code is doing. 直接翻译很少有例外,一种是文件名的检索,我不了解C ++代码的工作方式。 Another is printing time stamps as this is your requirement. 另一个是打印时间戳,因为这是您的要求。 Also, you need to add error checking and protection blocks for resources like the volume handle or memory, etc.. 另外,您需要为卷句柄或内存等资源添加错误检查和保护块。

The code uses the declarations from the helper unit you have included in the question. 代码使用问题中包含的帮助器单元中的声明。 I checked the time stamps against a 3rd party freeware utility which I found here and they are in accordance. 我对照在这里找到的第3方免费软件实用程序检查了时间戳,这些时间戳与它们一致。

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  windows,
  uMFT;

type
  USN = LONGLONG;
const
  BUF_LEN = 4096;
var
  hVol: THandle;
  JournalData: TUSNJournalData;
  dwBytes: DWORD;
  dwRetBytes: DWORD;
  ReadData: TReadUSNJournalData;
  i: Integer;
  Buffer: array [0..BUF_LEN - 1] of Byte;
  UsnRecord: PUSNRecord;
  FileName: PWideChar;
  SysTime: TSystemTime;
begin
  hVol := CreateFile( '\\.\c:', GENERIC_READ,
      FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);

  if hVol = INVALID_HANDLE_VALUE then begin
    Writeln(Format('CreateFile failed (%d)', [GetLastError]));
    Exit;
  end;

  if not DeviceIoControl(hVol, FSCTL_QUERY_USN_JOURNAL, nil, 0, @JournalData,
      SizeOf(JournalData), dwBytes, nil) then begin
    Writeln(Format('Query journal failed (%d)', [GetLastError]));
    Exit;
  end;

  ZeroMemory(@ReadData, SizeOf(ReadData));
  ReadData.ReasonMask := $FFFFFFFF;
  ReadData.UsnJournalID := JournalData.UsnJournalID;

  Writeln(Format('Journal ID: %x', [JournalData.UsnJournalID]));
  Writeln(Format('FirstUsn: %x' + sLineBreak, [JournalData.FirstUsn]));

  for i := 0 to 10 do begin
    FillChar(Buffer, BUF_LEN, 0);

    if not DeviceIoControl(hVol, FSCTL_READ_USN_JOURNAL, @ReadData,
        SizeOf(ReadData), @Buffer, BUF_LEN, dwBytes, nil) then begin
      Writeln(Format('Read journal failed (%d)', [GetLastError]));
      Exit;
    end;
    dwRetBytes := dwBytes - SizeOf(USN);

    // Find the first record
    UsnRecord := PUsnRecord(NativeInt(@Buffer) + SizeOf(USN));

    Writeln('****************************************');

    while dwRetBytes > 0 do begin

      Writeln(Format('USN: %x', [UsnRecord.Usn]));

      GetMem(FileName, UsnRecord.FileNameLength + SizeOf(Char));
      Move(Pointer(NativeInt(UsnRecord) + UsnRecord.FileNameOffset)^, FileName^,
          UsnRecord.FileNameLength);
      FileName[UsnRecord.FileNameLength div 2] := #0;
      Writeln(Format('File name: %s', [FileName]));
      FreeMem(FileName);

      FileTimeToSystemTime(@UsnRecord.TimeStamp, SysTime);
      Writeln(Format('Time stamp: %s', [DateTimeToStr(SystemTimeToDateTime(SysTime))]));
      Writeln(Format('Reason: %x', [UsnRecord.Reason]));
      Writeln;

      dwRetBytes := dwRetBytes - UsnRecord.RecordLength;

      // Find the next record
      UsnRecord := PUsnRecord(NativeInt(UsnRecord) + UsnRecord.RecordLength);
    end;
  end;
  CloseHandle(hVol);

  Writeln;
  Writeln('End of sample');
  Readln;
end.

The system returns 0, read "Remarks" for FSCTL_READ_FILE_USN_DATA on the page: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364584(v=vs.85).aspx CreateFile example: 系统返回0,在页面上读取FSCTL_READ_FILE_USN_DATA的“备注”: https ://msdn.microsoft.com/zh-cn/library/windows/desktop/aa364584(v = vs.85).aspx CreateFile示例:

H: THandle;

[...] [...]

H := CreateFile(PWChar('\\.\' + DiskName + ':\' + FileName),
    GENERIC_READ, FILE_SHARE_READ, nil, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, 0);

[...] [...]

if (H = INVALID_HANDLE_VALUE) then //GetLastError()
  Memo.Lines.Add(FileName + ' 0')
else
begin
  GetfileTime(H, @FTime, nil, nil);
  Memo.Lines.Add(FileName + ' ' + DateTimeToStr(FileTimeToDateTime(FTime)));
end;

[...] [...]

CloseHandle(H);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM