/*++ Copyright (c) 2000 Microsoft Corporation Module Name: usn.c Abstract: This file contains code for commands that affect the usn journal. Author: Wesley Witt [wesw] 1-March-2000 Revision History: --*/ #include #define MAX_USN_DATA 4096 // // Arguments and lengths // #define ARG_MAXSIZE L"m=" #define ARG_MAXSIZE_LEN 2 #define ARG_ALLOCDELTA L"a=" #define ARG_ALLOCDELTA_LEN 2 INT UsnHelp( IN INT argc, IN PWSTR argv[] ) { DisplayMsg( MSG_USAGE_USN ); return EXIT_CODE_SUCCESS; } __inline PUSN_RECORD NextUsnRecord( const PUSN_RECORD input ) { ULONGLONG output; // Get the base address of the current record. (PUSN_RECORD) output = input; // Add the size of the record (structure + file name after the end // of the structure). output += input->RecordLength; // Round up the record size to match the 64-bit alignment, if the // size is not already a multiple of 8. Perform a bitwise AND // operation here instead of division because it is much faster than // division. However, the bitwise AND operation only works because // the divisor 8 is a power of 2. if (output & 8-1) { // Round down to nearest multiple of 8. output &= -8; // Then add 8. output += 8; } return((PUSN_RECORD) output); } VOID DisplayUsnRecord( const PUSN_RECORD UsnRecord ) { WCHAR DateString[128]; WCHAR TimeString[128]; TIME_FIELDS TimeFields; SYSTEMTIME SystemTime; RtlTimeToTimeFields(&UsnRecord->TimeStamp, &TimeFields); SystemTime.wYear = TimeFields.Year ; SystemTime.wMonth = TimeFields.Month ; SystemTime.wDayOfWeek = TimeFields.Weekday ; SystemTime.wDay = TimeFields.Day ; SystemTime.wHour = TimeFields.Hour ; SystemTime.wMinute = TimeFields.Minute ; SystemTime.wSecond = TimeFields.Second ; SystemTime.wMilliseconds = TimeFields.Milliseconds; GetDateFormat( LOCALE_USER_DEFAULT, DATE_SHORTDATE, &SystemTime, NULL, DateString, sizeof( DateString ) / sizeof( DateString[0] )); GetTimeFormat( LOCALE_USER_DEFAULT, FALSE, &SystemTime, NULL, TimeString, sizeof( TimeString ) / sizeof( TimeString[0] )); DisplayMsg( MSG_USNRECORD, UsnRecord->MajorVersion, UsnRecord->MinorVersion, QuadToPaddedHexText( UsnRecord->FileReferenceNumber ), QuadToPaddedHexText( UsnRecord->ParentFileReferenceNumber ), QuadToPaddedHexText( UsnRecord->Usn ), QuadToPaddedHexText( UsnRecord->TimeStamp.QuadPart ), TimeString, DateString, UsnRecord->Reason, UsnRecord->SourceInfo, UsnRecord->SecurityId, UsnRecord->FileAttributes, UsnRecord->FileNameLength, UsnRecord->FileNameOffset, UsnRecord->FileNameLength/sizeof(WCHAR), UsnRecord->FileName ); } INT CreateUsnJournal( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine create the USN journal for the volume specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl crusnj m= a= '. Return Value: None --*/ { HANDLE FileHandle = INVALID_HANDLE_VALUE; WCHAR FileName[MAX_PATH]; BOOL Status; DWORD BytesReturned; CREATE_USN_JOURNAL_DATA InBuffer; ULONGLONG MaxSize; ULONGLONG AllocDelta; PWSTR EndPtr; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc != 3) { DisplayMsg( MSG_USAGE_CREATEUSN ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } if (!IsVolumeLocalNTFS( argv[2][0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } if (wcslen( DotPrefix ) + wcslen( argv[2] ) + 1 > MAX_PATH) { DisplayMsg( MSG_FILENAME_TOO_LONG ); ExitCode = EXIT_CODE_FAILURE; leave; } wcscpy( FileName, DotPrefix ); wcscat( FileName, argv[2] ); FileHandle = CreateFile( FileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } if (_wcsnicmp( argv[0], ARG_MAXSIZE, ARG_MAXSIZE_LEN) || wcslen( argv[0] ) == ARG_MAXSIZE_LEN) { DisplayMsg( MSG_INVALID_PARAMETER, argv[0] ); DisplayMsg( MSG_USAGE_CREATEUSN ); ExitCode = EXIT_CODE_FAILURE; leave; } if (_wcsnicmp( argv[1], ARG_ALLOCDELTA, ARG_ALLOCDELTA_LEN) || wcslen( argv[1] ) == ARG_ALLOCDELTA_LEN) { DisplayMsg( MSG_INVALID_PARAMETER, argv[1] ); DisplayMsg( MSG_USAGE_CREATEUSN ); ExitCode = EXIT_CODE_FAILURE; leave; } MaxSize = My_wcstoui64( argv[0] + ARG_MAXSIZE_LEN, &EndPtr, 0 ); if (UnsignedI64NumberCheck( MaxSize, EndPtr )) { DisplayMsg( MSG_INVALID_PARAMETER, argv[0] ); DisplayMsg( MSG_USAGE_CREATEUSN ); ExitCode = EXIT_CODE_FAILURE; leave; } AllocDelta = My_wcstoui64( argv[1] + ARG_ALLOCDELTA_LEN, &EndPtr, 0 ); if (UnsignedI64NumberCheck( AllocDelta, EndPtr )) { DisplayMsg( MSG_INVALID_PARAMETER, argv[1] ); DisplayMsg( MSG_USAGE_CREATEUSN ); ExitCode = EXIT_CODE_FAILURE; leave; } InBuffer.MaximumSize = MaxSize; InBuffer.AllocationDelta = AllocDelta; Status = DeviceIoControl( FileHandle, FSCTL_CREATE_USN_JOURNAL, &InBuffer, sizeof(InBuffer), NULL, 0, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } } return ExitCode; } INT QueryUsnJournal( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine queries the USN journal for the volume specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl queryusnj '. Return Value: None --*/ { HANDLE FileHandle = INVALID_HANDLE_VALUE; WCHAR FileName[MAX_PATH]; BOOL Status; DWORD BytesReturned; USN_JOURNAL_DATA UsnJournalData; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc != 1) { DisplayMsg( MSG_USAGE_QUERYUSN ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } if (!IsVolumeLocalNTFS( argv[0][0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } if (wcslen( DotPrefix ) + wcslen( argv[0] ) + 1 > MAX_PATH) { DisplayMsg( MSG_FILENAME_TOO_LONG ); ExitCode = EXIT_CODE_FAILURE; leave; } wcscpy( FileName, DotPrefix ); wcscat( FileName, argv[0] ); FileHandle = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } Status = DeviceIoControl( FileHandle, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &UsnJournalData, sizeof(USN_JOURNAL_DATA), &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } DisplayMsg( MSG_QUERYUSN, QuadToPaddedHexText( UsnJournalData.UsnJournalID ), QuadToPaddedHexText( UsnJournalData.FirstUsn ), QuadToPaddedHexText( UsnJournalData.NextUsn ), QuadToPaddedHexText( UsnJournalData.LowestValidUsn ), QuadToPaddedHexText( UsnJournalData.MaxUsn ), QuadToPaddedHexText( UsnJournalData.MaximumSize ), QuadToPaddedHexText( UsnJournalData.AllocationDelta ) ); } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } } return ExitCode; } INT DeleteUsnJournal( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine deletes the USN journal for the volume specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl delusnj '. Return Value: None --*/ { HANDLE FileHandle = INVALID_HANDLE_VALUE; WCHAR FileName[MAX_PATH]; BOOL Status; DWORD BytesReturned; DELETE_USN_JOURNAL_DATA DeleteUsnJournalData; USN_JOURNAL_DATA UsnJournalData; INT i; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc < 2) { DisplayMsg( MSG_USAGE_DELETEUSN ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } if (!IsVolumeLocalNTFS( argv[argc-1][0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } if (wcslen( DotPrefix ) + wcslen( argv[argc - 1] ) + 1 > MAX_PATH) { DisplayMsg( MSG_FILENAME_TOO_LONG ); ExitCode = EXIT_CODE_FAILURE; leave; } wcscpy( FileName, DotPrefix ); wcscat( FileName, argv[argc - 1] ); FileHandle = CreateFile( FileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } Status = DeviceIoControl( FileHandle, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &UsnJournalData, sizeof(USN_JOURNAL_DATA), &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } DeleteUsnJournalData.DeleteFlags = USN_DELETE_FLAG_DELETE ; for (i = 0; i < argc - 1; i++) { if (argv[i][0] == L'/' && wcslen( argv[i] ) == 2) { switch (towupper( argv[i][1] ) ) { case L'D': DeleteUsnJournalData.DeleteFlags |= USN_DELETE_FLAG_DELETE ; continue; case L'N': DeleteUsnJournalData.DeleteFlags |= USN_DELETE_FLAG_NOTIFY ; continue; } } DisplayMsg( MSG_INVALID_PARAMETER, argv[i] ); DisplayMsg( MSG_USAGE_DELETEUSN ); ExitCode = EXIT_CODE_FAILURE; leave; } DeleteUsnJournalData.UsnJournalID = UsnJournalData.UsnJournalID; Status = DeviceIoControl( FileHandle, FSCTL_DELETE_USN_JOURNAL, &DeleteUsnJournalData, sizeof(DELETE_USN_JOURNAL_DATA), NULL, 0, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } } return ExitCode; } INT EnumUsnData( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine enumerated the USN data associated with the volume specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl enusndata '. Return Value: None --*/ { HANDLE FileHandle = INVALID_HANDLE_VALUE; WCHAR FileName[MAX_PATH]; BOOL Status; DWORD BytesReturned; MFT_ENUM_DATA MftEnumData; PVOID lpOutBuffer = NULL; DWORD nOutBufferSize; PUSN_RECORD UsnRecord; WORD Index; LONG Length; PWSTR EndStr; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc != 4) { DisplayMsg( MSG_USAGE_ENUMDATA ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } if (!IsVolumeLocalNTFS( argv[argc-1][0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } if (wcslen( DotPrefix ) + wcslen( argv[argc - 1] ) + 1 > MAX_PATH) { DisplayMsg( MSG_FILENAME_TOO_LONG ); ExitCode = EXIT_CODE_FAILURE; leave; } wcscpy( FileName, DotPrefix ); wcscat( FileName, argv[argc - 1] ); FileHandle = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } nOutBufferSize = MAX_USN_DATA; lpOutBuffer = (PVOID) malloc ( nOutBufferSize ); MftEnumData.StartFileReferenceNumber = My_wcstoui64( argv[0], &EndStr, 0 ); if (UnsignedI64NumberCheck( MftEnumData.StartFileReferenceNumber, EndStr )) { DisplayMsg( MSG_USAGE_ENUMDATA ); ExitCode = EXIT_CODE_FAILURE; leave; } MftEnumData.LowUsn = My_wcstoui64( argv[1], &EndStr, 0 ); if (UnsignedI64NumberCheck( MftEnumData.LowUsn, EndStr )) { DisplayMsg( MSG_USAGE_ENUMDATA ); ExitCode = EXIT_CODE_FAILURE; leave; } MftEnumData.HighUsn = My_wcstoui64( argv[2], &EndStr, 0 ); if (UnsignedI64NumberCheck( MftEnumData.HighUsn, EndStr )) { DisplayMsg( MSG_USAGE_ENUMDATA ); ExitCode = EXIT_CODE_FAILURE; leave; } while (TRUE) { Status = DeviceIoControl( FileHandle, FSCTL_ENUM_USN_DATA, &MftEnumData, sizeof(MFT_ENUM_DATA), lpOutBuffer, nOutBufferSize, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { if (GetLastError() != ERROR_HANDLE_EOF) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; } leave; } if ( BytesReturned < sizeof( ULONGLONG ) + sizeof( USN_RECORD )) { break; } UsnRecord = (PUSN_RECORD) ((PBYTE)lpOutBuffer + sizeof( ULONGLONG )); while ((PBYTE)UsnRecord < (PBYTE)lpOutBuffer + BytesReturned) { DisplayMsg( MSG_ENUMDATA, QuadToPaddedHexText( UsnRecord->FileReferenceNumber ), QuadToPaddedHexText( UsnRecord->ParentFileReferenceNumber ), QuadToPaddedHexText( UsnRecord->Usn ), UsnRecord->SecurityId, UsnRecord->Reason, UsnRecord->FileNameLength, UsnRecord->FileNameLength / sizeof(WCHAR), UsnRecord->FileName ); UsnRecord = NextUsnRecord( UsnRecord ); } MftEnumData.StartFileReferenceNumber = *(PLONGLONG)lpOutBuffer; } } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } if (lpOutBuffer) { free( lpOutBuffer ); } } return ExitCode; } INT ReadFileUsnData( IN INT argc, IN PWSTR argv[] ) /*++ Routine Description: This routine reads the usn data for the volume specified. Arguments: argc - The argument count. argv - Array of Strings of the form : ' fscutl rdusndata '. Return Value: None --*/ { HANDLE FileHandle = INVALID_HANDLE_VALUE; WCHAR FileName[MAX_PATH]; BOOL Status; DWORD BytesReturned; DWORD nOutBufferSize; PUSN_RECORD UsnRecord = NULL; PWSTR FullName; INT ExitCode = EXIT_CODE_SUCCESS; try { if (argc != 1) { DisplayMsg( MSG_USAGE_READDATA ); if (argc != 0) { ExitCode = EXIT_CODE_FAILURE; } leave; } FullName = GetFullPath( argv[0] ); if (!FullName) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } if (!IsVolumeLocalNTFS( FullName[0] )) { DisplayMsg( MSG_NTFS_REQUIRED ); ExitCode = EXIT_CODE_FAILURE; leave; } if (wcslen( DotPrefix ) + wcslen( FullName ) + 1 > MAX_PATH) { DisplayMsg( MSG_FILENAME_TOO_LONG ); ExitCode = EXIT_CODE_FAILURE; leave; } wcscpy( FileName, DotPrefix ); wcscat( FileName, FullName ); FileHandle = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (FileHandle == INVALID_HANDLE_VALUE) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } nOutBufferSize = MAX_USN_DATA; UsnRecord = (PUSN_RECORD) malloc ( nOutBufferSize ); Status = DeviceIoControl( FileHandle, FSCTL_READ_FILE_USN_DATA, NULL, 0, UsnRecord, nOutBufferSize, &BytesReturned, (LPOVERLAPPED)NULL ); if (!Status) { DisplayError(); ExitCode = EXIT_CODE_FAILURE; leave; } DisplayUsnRecord( UsnRecord ); } finally { if (FileHandle != INVALID_HANDLE_VALUE) { CloseHandle( FileHandle ); } if (UsnRecord) { free( UsnRecord ); } free( FullName ); } return ExitCode; }