[英]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.