/*++ © 1998 Seagate Software, Inc. All rights reserved Module Name: WsbUsn.cpp Abstract: Functions to manipulate the USN journal and USN records on a file Author: Rick Winter [rick] 11-17-97 Revision History: --*/ #include "stdafx.h" #define HSM_FILE_CHANGED (USN_REASON_DATA_OVERWRITE | USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION | USN_REASON_FILE_DELETE) // Local functions static HANDLE OpenVol(OLECHAR* volName); HRESULT WsbCheckUsnJournalForChanges( OLECHAR* volName, ULONGLONG fileUsnJournalId, LONGLONG FileId, LONGLONG StartUsn, LONGLONG StopUsn, BOOL* pChanged ) /*++ Routine Description: Check the USN Journal for changes to the unnamed data stream for this file between the given USNs. Arguments: volName - Volume name FileId - File ID of file StartUsn - USN to start at in journal StopUsn - USN to stop at in journal pChanged - Pointer to result: TRUE for change Return Value: S_OK - success --*/ { ULONGLONG Buffer[1024]; HRESULT hr = S_OK; IO_STATUS_BLOCK Iosb; USN NextUsn; NTSTATUS Status; READ_USN_JOURNAL_DATA ReadUsnJournalData; DWORD ReturnedByteCount; ULONGLONG usnId; PUSN_RECORD pUsnRecord; HANDLE volHandle = INVALID_HANDLE_VALUE; WsbTraceIn(OLESTR("WsbCheckUsnJournalForChanges"), OLESTR("volName = %ls, FileId = %I64x, StartUsn = %I64d, StopUsn = %I64d"), volName, FileId, StartUsn, StopUsn); try { WsbAffirmPointer(pChanged); *pChanged = FALSE; volHandle = OpenVol(volName); WsbAffirmHandle(volHandle); // Get the journal ID WsbAffirmHr(WsbGetUsnJournalId(volName, &usnId)); // If we got a non-zero journal id, cpmare to the current one and fail if they are not equal if ((fileUsnJournalId != 0) && (fileUsnJournalId != usnId)) { WsbTraceAlways( OLESTR("WsbCheckUsnJournalForChanges: Current Usn journal id %I64x doesn't match file Usn journal id %I64x\n"), usnId, fileUsnJournalId); WsbThrow(E_FAIL); } // Set up read info NextUsn = StartUsn; ReadUsnJournalData.UsnJournalID = usnId; ReadUsnJournalData.ReasonMask = HSM_FILE_CHANGED; ReadUsnJournalData.ReturnOnlyOnClose = TRUE; ReadUsnJournalData.Timeout = 0; // ???? ReadUsnJournalData.BytesToWaitFor = 0; // ?????? // Loop through journal entries while (!*pChanged) { ReadUsnJournalData.StartUsn = NextUsn; Status = NtFsControlFile( volHandle, NULL, NULL, NULL, &Iosb, FSCTL_READ_USN_JOURNAL, &ReadUsnJournalData, sizeof(ReadUsnJournalData), &Buffer, sizeof(Buffer) ); if (NT_SUCCESS(Status)) { Status = Iosb.Status; } if (Status == STATUS_JOURNAL_ENTRY_DELETED) { WsbTrace(OLESTR("WsbCheckUsnJournalForChanges: StartUsn has been deleted\n")); } WsbAffirmNtStatus(Status); ReturnedByteCount = (DWORD)Iosb.Information; WsbTrace(OLESTR("WsbCheckUsnJournalForChanges: bytes read = %u\n"), ReturnedByteCount); // Get the next USN start point & and the first // journal entry NextUsn = *(USN *)&Buffer; pUsnRecord = (PUSN_RECORD)((PCHAR)&Buffer + sizeof(USN)); ReturnedByteCount -= sizeof(USN); // Make sure we actually got some entries if (0 == ReturnedByteCount) { WsbTrace(OLESTR("WsbCheckUsnJournalForChanges: no entries, exiting loop\n"), ReturnedByteCount); break; } // Loop over entries in this buffer while (ReturnedByteCount != 0) { WsbAffirm(pUsnRecord->RecordLength <= ReturnedByteCount, E_FAIL); // Skip the first record and check for match on File Id // (Also skip entries that we created) if (pUsnRecord->Usn > StartUsn && USN_SOURCE_DATA_MANAGEMENT != pUsnRecord->SourceInfo && pUsnRecord->FileReferenceNumber == static_cast(FileId)) { WsbTrace(OLESTR("WsbCheckUsnJournalForChanges: found change record\n")); WsbTrace(OLESTR( " Reason: %08lx\n"), pUsnRecord->Reason); *pChanged = TRUE; break; } else if (pUsnRecord->Usn == StartUsn) { // This check is done to make sure the journal is valid - // StartUsn record must refer to the file in question if (pUsnRecord->FileReferenceNumber != static_cast(FileId)) { WsbTraceAlways( OLESTR("WsbCheckUsnJournalForChanges: StartUsn %I64d for FileId %I64x doesn't match usn journal FileId %I64x\n"), StartUsn, FileId, pUsnRecord->FileReferenceNumber); WsbThrow(E_FAIL); } } ReturnedByteCount -= pUsnRecord->RecordLength; pUsnRecord = (PUSN_RECORD)((PCHAR)pUsnRecord + pUsnRecord->RecordLength); } // Make sure we're making progress WsbAffirm(NextUsn > ReadUsnJournalData.StartUsn, E_FAIL); } } WsbCatch( hr ); if (INVALID_HANDLE_VALUE != volHandle) { CloseHandle(volHandle); } WsbTraceOut(OLESTR("WsbCheckUsnJournalForChanges"), OLESTR("Hr = <%ls>, Changed = %ls"), WsbHrAsString(hr), WsbBoolAsString(*pChanged)); return( hr ); } HRESULT WsbGetUsnFromFileHandle( IN HANDLE hFile, IN BOOL ForceClose, OUT LONGLONG* pFileUsn ) /*++ Routine Description: Get the current USN Journal number for the open file. Arguments: hFile - Handle to the open file pFileUsn - Pointer to File USN to be returned. Return Value: S_OK - success --*/ { HRESULT hr = S_OK; WsbTraceIn(OLESTR("WsbGetUsnFromFileHandle"), OLESTR("")); try { char buffer[4096]; IO_STATUS_BLOCK IoStatusBlock; PUSN_RECORD pUsnInfo; WsbAffirm(pFileUsn, E_POINTER); *pFileUsn = 0; if (TRUE == ForceClose) { // Get the internal information WsbAffirmNtStatus(NtFsControlFile( hFile, NULL, NULL, NULL, &IoStatusBlock, FSCTL_WRITE_USN_CLOSE_RECORD, NULL, 0, buffer, sizeof(buffer))); } // Get the internal information WsbAffirmNtStatus(NtFsControlFile( hFile, NULL, NULL, NULL, &IoStatusBlock, FSCTL_READ_FILE_USN_DATA, NULL, 0, buffer, sizeof(buffer))); pUsnInfo = (PUSN_RECORD) buffer; WsbTrace(OLESTR("WsbGetUsnFromFileHandle, Usn record version number is %u\n"), pUsnInfo->MajorVersion); // Check the version WsbAffirm(pUsnInfo->MajorVersion == 2, WSB_E_INVALID_DATA); // Get the USN *pFileUsn = pUsnInfo->Usn; } WsbCatchAndDo(hr, WsbTrace(OLESTR("WsbGetUsnFromFileHandle, GetLastError = %lx\n"), GetLastError()); ); WsbTraceOut(OLESTR("WsbGetUsnFromFileHandle"), OLESTR("Hr = <%ls>, FileUsn = %I64d"), WsbHrAsString(hr), *pFileUsn); return(hr); } HRESULT WsbMarkUsnSource( HANDLE changeHandle, OLECHAR* volName ) /*++ Routine Description: Mark the source of file changes for this handle as data management. This lets others, such as content indexing, know that the changes do not affect file content. Arguments: changeHandle - Handle to the open file volName - Volume name (d:\) Return Value: S_OK - success --*/ { HRESULT hr = S_OK; HANDLE volHandle = INVALID_HANDLE_VALUE; NTSTATUS ntStatus; MARK_HANDLE_INFO sInfo; IO_STATUS_BLOCK IoStatusBlock; try { volHandle = OpenVol(volName); WsbAffirmHandle(volHandle); sInfo.UsnSourceInfo = USN_SOURCE_DATA_MANAGEMENT; sInfo.VolumeHandle = volHandle; sInfo.HandleInfo = 0; ntStatus = NtFsControlFile( changeHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_MARK_HANDLE, &sInfo, sizeof(MARK_HANDLE_INFO), NULL, 0); WsbAffirmNtStatus(ntStatus); CloseHandle(volHandle); volHandle = INVALID_HANDLE_VALUE; } WsbCatch( hr ); if (INVALID_HANDLE_VALUE != volHandle) { CloseHandle(volHandle); } return( hr ); } HRESULT WsbCreateUsnJournal( OLECHAR* volName, ULONGLONG usnSize ) /*++ Routine Description: Create the USN journal for the given volume. Arguments: volName - Volume name (d:\) usnSize - Max size of journal Return Value: S_OK - success --*/ { HRESULT hr = S_OK; HANDLE volHandle = INVALID_HANDLE_VALUE; NTSTATUS ntStatus; IO_STATUS_BLOCK IoStatusBlock; CREATE_USN_JOURNAL_DATA CreateUsnJournalData; WsbTraceIn(OLESTR("WsbCreateUsnJournal"), OLESTR("volName = %ls, Size = %I64d"), volName, usnSize); try { volHandle = OpenVol(volName); WsbAffirmHandle(volHandle); CreateUsnJournalData.MaximumSize = usnSize; CreateUsnJournalData.AllocationDelta = usnSize / 32; ntStatus = NtFsControlFile( volHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_CREATE_USN_JOURNAL, &CreateUsnJournalData, sizeof(CreateUsnJournalData), NULL, 0); WsbTrace(OLESTR("WsbCreateUsnJournal: ntStatus = %lx, iosb.Status = %lx\n"), ntStatus, IoStatusBlock.Status); if (STATUS_DISK_FULL == ntStatus) { WsbThrow(WSB_E_USNJ_CREATE_DISK_FULL); } else if (!NT_SUCCESS(ntStatus)) { WsbThrow(WSB_E_USNJ_CREATE); } WsbAffirmNtStatus(ntStatus); } WsbCatch( hr ); if (INVALID_HANDLE_VALUE != volHandle) { CloseHandle(volHandle); } WsbTraceOut(OLESTR("WsbCreateUsnJournal"), OLESTR("Hr = <%ls>"), WsbHrAsString(hr)); return( hr ); } HRESULT WsbGetUsnJournalId( OLECHAR* volName, ULONGLONG* usnId ) /*++ Routine Description: Get the current USN Journal ID Arguments: volName - Volume name (d:\) usnId - Id is returned here. Return Value: S_OK - success --*/ { HRESULT hr = S_OK; HANDLE volHandle = INVALID_HANDLE_VALUE; NTSTATUS ntStatus; IO_STATUS_BLOCK IoStatusBlock; USN_JOURNAL_DATA usnData; WsbTraceIn(OLESTR("WsbGetUsnJournalId"), OLESTR("volName = %ls"), volName); try { WsbAffirmPointer(usnId); volHandle = OpenVol(volName); WsbAffirmHandle(volHandle); *usnId = (ULONGLONG) 0; ntStatus = NtFsControlFile( volHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_QUERY_USN_JOURNAL, NULL, 0, &usnData, sizeof(usnData)); WsbTrace(OLESTR("WsbGetUsnJournalId: ntStatus = %lx, iosb.Status = %lx\n"), ntStatus, IoStatusBlock.Status); if (STATUS_JOURNAL_NOT_ACTIVE == ntStatus) { WsbThrow(WSB_E_NOTFOUND); } WsbAffirmNtStatus(ntStatus); *usnId = usnData.UsnJournalID; } WsbCatch( hr ); if (INVALID_HANDLE_VALUE != volHandle) { CloseHandle(volHandle); } WsbTraceOut(OLESTR("WsbGetUsnJournalId"), OLESTR("Hr = <%ls>, id = %I64x"), WsbHrAsString(hr), *usnId); return( hr ); } // Local functions static HANDLE OpenVol(OLECHAR* volName) { HRESULT hr = S_OK; HANDLE volHandle = INVALID_HANDLE_VALUE; CWsbStringPtr name; WCHAR *vPtr; try { name = volName; if (name == NULL) { WsbThrow(E_OUTOFMEMORY); } if (name[1] == L':') { swprintf((OLECHAR*) name, L"%2.2s", volName); } else { // // Must be a volume without a drive letter // Move to end of PNPVolumeName... vPtr = name; vPtr = wcsrchr(vPtr, L'\\'); if (NULL != vPtr) { *vPtr = L'\0'; } } WsbAffirmHr(name.Prepend(OLESTR("\\\\.\\"))); WsbAffirmHandle(volHandle = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL )); } WsbCatchAndDo( hr, if (INVALID_HANDLE_VALUE != volHandle) { CloseHandle(volHandle); } volHandle = INVALID_HANDLE_VALUE; ) return(volHandle); }