public UsnJournalReturnCode MonitorUsnJournalEntries(UInt32 reasonMask) { var state = new Win32.USN_JOURNAL_DATA(); if (!IsNtfsVolume) { return UsnJournalReturnCode.VOLUME_NOT_NTFS; } if (_usnJournalRootHandle.ToInt32() == Win32.INVALID_HANDLE_VALUE) { return UsnJournalReturnCode.INVALID_HANDLE_VALUE; } var returnCode = QueryUsnJournal(ref state); if (returnCode != UsnJournalReturnCode.USN_JOURNAL_SUCCESS) { return returnCode; } const int pbDataSize = sizeof (UInt64)*0x4000; IntPtr pbData = Marshal.AllocHGlobal(pbDataSize); Win32.ZeroMemory(pbData, pbDataSize); var rujd = new Win32.READ_USN_JOURNAL_DATA { StartUsn = state.NextUsn, ReasonMask = reasonMask, ReturnOnlyOnClose = 0, Timeout = 1, bytesToWaitFor = 1, UsnJournalId = state.UsnJournalID }; int sizeRujd = Marshal.SizeOf(rujd); IntPtr rujdBuffer = Marshal.AllocHGlobal(sizeRujd); Win32.ZeroMemory(rujdBuffer, sizeRujd); Marshal.StructureToPtr(rujd, rujdBuffer, true); _readMore = true; // Read USN journal entries while (_readMore) { uint bytesReturned; bool returnValue = Win32.DeviceIoControl( _usnJournalRootHandle, Win32.FSCTL_READ_USN_JOURNAL, rujdBuffer, sizeRujd, pbData, pbDataSize, out bytesReturned, IntPtr.Zero); if (!returnValue) { var lastWin32Error = (Win32.GetLastErrorEnum) Marshal.GetLastWin32Error(); Marshal.FreeHGlobal(rujdBuffer); Marshal.FreeHGlobal(pbData); return lastWin32Error == Win32.GetLastErrorEnum.ERROR_HANDLE_EOF ? UsnJournalReturnCode.USN_JOURNAL_SUCCESS : ConvertWin32ErrorToUsnError(lastWin32Error); } var usnEntryPointer = new IntPtr(pbData.ToInt32() + sizeof (UInt64)); // While there are at least one entry in the usn journal // XXX: Why magic number 60? while (bytesReturned > 60) { var usnEntry = new Win32.UsnEntry(usnEntryPointer); OnNewUsnRecord(new NewUsnRecordEventArgs(usnEntry)); usnEntryPointer = new IntPtr(usnEntryPointer.ToInt32() + usnEntry.RecordLength); bytesReturned -= usnEntry.RecordLength; } var nextUsn = Marshal.ReadInt64(pbData, 0); Marshal.WriteInt64(rujdBuffer, nextUsn); } Marshal.FreeHGlobal(rujdBuffer); Marshal.FreeHGlobal(pbData); return returnCode; }
/// <summary> /// Given a previous state, GetUsnJournalEntries() determines if the USN Journal is active and /// no USN Journal entries have been lost (i.e. USN Journal is valid), then /// it loads a SortedList<UInt64, Win32.UsnEntry> list and returns it as the out parameter 'usnEntries'. /// If GetUsnJournalChanges returns anything but USN_JOURNAL_SUCCESS, the usnEntries list will /// be empty. /// </summary> /// <param name="previousUsnState">The USN Journal state the last time volume /// changes were requested.</param> /// <param name="reasonMask"></param> /// <param name="usnEntries"></param> /// <param name="newUsnState"></param> /// <returns> /// USN_JOURNAL_SUCCESS GetUsnJournalChanges() function succeeded. /// VOLUME_NOT_NTFS volume is not an NTFS volume. /// INVALID_HANDLE_VALUE NtfsUsnJournal object failed initialization. /// USN_JOURNAL_NOT_ACTIVE usn journal is not active on volume. /// ERROR_ACCESS_DENIED accessing the usn journal requires admin rights, see remarks. /// ERROR_INVALID_FUNCTION error generated by DeviceIoControl() call. /// ERROR_FILE_NOT_FOUND error generated by DeviceIoControl() call. /// ERROR_PATH_NOT_FOUND error generated by DeviceIoControl() call. /// ERROR_TOO_MANY_OPEN_FILES error generated by DeviceIoControl() call. /// ERROR_INVALID_HANDLE error generated by DeviceIoControl() call. /// ERROR_INVALID_DATA error generated by DeviceIoControl() call. /// ERROR_NOT_SUPPORTED error generated by DeviceIoControl() call. /// ERROR_INVALID_PARAMETER error generated by DeviceIoControl() call. /// ERROR_JOURNAL_DELETE_IN_PROGRESS usn journal delete is in progress. /// ERROR_INVALID_USER_BUFFER error generated by DeviceIoControl() call. /// USN_JOURNAL_ERROR unspecified usn journal error. /// </returns> /// <remarks> /// If function returns ERROR_ACCESS_DENIED you need to run application as an Administrator. /// </remarks> public UsnJournalReturnCode GetUsnJournalEntries(Win32.USN_JOURNAL_DATA previousUsnState, UInt32 reasonMask, out List<Win32.UsnEntry> usnEntries, out Win32.USN_JOURNAL_DATA newUsnState) { usnEntries = new List<Win32.UsnEntry>(); newUsnState = new Win32.USN_JOURNAL_DATA(); UsnJournalReturnCode returnCode = UsnJournalReturnCode.VOLUME_NOT_NTFS; if (IsNtfsVolume) { if (_usnJournalRootHandle.ToInt32() != Win32.INVALID_HANDLE_VALUE) { // get current usn journal state returnCode = QueryUsnJournal(ref newUsnState); if (returnCode == UsnJournalReturnCode.USN_JOURNAL_SUCCESS) { bool bReadMore = true; // sequentially process the usn journal looking for image file entries int pbDataSize = sizeof(UInt64) * 0x4000; IntPtr pbData = Marshal.AllocHGlobal(pbDataSize); Win32.ZeroMemory(pbData, pbDataSize); Win32.READ_USN_JOURNAL_DATA rujd = new Win32.READ_USN_JOURNAL_DATA(); rujd.StartUsn = previousUsnState.NextUsn; rujd.ReasonMask = reasonMask; rujd.ReturnOnlyOnClose = 0; rujd.Timeout = 0; rujd.bytesToWaitFor = 0; rujd.UsnJournalId = previousUsnState.UsnJournalID; int sizeRujd = Marshal.SizeOf(rujd); IntPtr rujdBuffer = Marshal.AllocHGlobal(sizeRujd); Win32.ZeroMemory(rujdBuffer, sizeRujd); Marshal.StructureToPtr(rujd, rujdBuffer, true); // // read usn journal entries // while (bReadMore) { uint outBytesReturned; bool bRtn = Win32.DeviceIoControl( _usnJournalRootHandle, Win32.FSCTL_READ_USN_JOURNAL, rujdBuffer, sizeRujd, pbData, pbDataSize, out outBytesReturned, IntPtr.Zero); if (bRtn) { var pUsnRecord = new IntPtr(pbData.ToInt32() + sizeof(UInt64)); while (outBytesReturned > 60) // while there are at least one entry in the usn journal { var usnEntry = new Win32.UsnEntry(pUsnRecord); if (usnEntry.USN >= newUsnState.NextUsn) // only read until the current usn points beyond the current state's usn { bReadMore = false; break; } usnEntries.Add(usnEntry); pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usnEntry.RecordLength); outBytesReturned -= usnEntry.RecordLength; } } else { var lastWin32Error = (Win32.GetLastErrorEnum)Marshal.GetLastWin32Error(); if (lastWin32Error == Win32.GetLastErrorEnum.ERROR_HANDLE_EOF) { returnCode = UsnJournalReturnCode.USN_JOURNAL_SUCCESS; } else { returnCode = ConvertWin32ErrorToUsnError(lastWin32Error); } break; } Int64 nextUsn = Marshal.ReadInt64(pbData, 0); if (nextUsn >= newUsnState.NextUsn) { break; } Marshal.WriteInt64(rujdBuffer, nextUsn); } Marshal.FreeHGlobal(rujdBuffer); Marshal.FreeHGlobal(pbData); } } else { returnCode = UsnJournalReturnCode.INVALID_HANDLE_VALUE; } } return returnCode; }