Scripts/Get-UsnJournalEntry.ps1

Function Get-UsnJournalEntry {
    <#
        .SYNOPSIS
            Views the entries of the Usn Journal. Also allows the use of -Wait to
            watch incoming entries.
 
        .DESCRIPTION
            Views the entries of the Usn Journal. Includes full file path and also allows
            the use of -Wait to watch incoming entries.
 
        .PARAMETER DriveLetter
            Drive that will used to look at journal entries
         
        .PARAMETER ReasonMask
            Possble values to use to look at various journal entries. Possible
            values are:
 
            USN_REASON_DATA_OVERWRITE
            USN_REASON_DATA_EXTEND
            USN_REASON_DATA_TRUNCATION
            USN_REASON_NAMED_DATA_OVERWRITE
            USN_REASON_NAMED_DATA_EXTEND
            USN_REASON_NAMED_DATA_TRUNCATION
            USN_REASON_FILE_CREATE
            USN_REASON_FILE_DELETE
            USN_REASON_EA_CHANGE
            USN_REASON_SECURITY_CHANGE
            USN_REASON_RENAME_OLD_NAME
            USN_REASON_RENAME_NEW_NAME
            USN_REASON_INDEXABLE_CHANGE
            USN_REASON_BASIC_INFO_CHANGE
            USN_REASON_HARD_LINK_CHANGE
            USN_REASON_COMPRESSION_CHANGE
            USN_REASON_ENCRYPTION_CHANGE
            USN_REASON_OBJECT_ID_CHANGE
            USN_REASON_REPARSE_POINT_CHANGE
            USN_REASON_STREAM_CHANGE
            USN_REASON_CLOSE
 
        .PARAMETER StartUsn
            The starting USN number to begin walking the journal. If this isn't an exact match,
            an error will occur. It is recommended to either set this to 0 or look at the
            NextUSN from Get-UsnJournal to use.
 
        .PARAMETER Tail
            Starts watching the journal at the NextUsn number. It is best to use this
            in conjunction with -Wait.
 
        .PARAMETER Wait
            This will watch for new entries in the Usn Journal once it has reached the end of the current
            journal entries. This can be used with -Tail to begin tracking the current entries being
            updated in the journal.
 
        .PARAMETER Paging
            This allows for paging of the journal entries based on the available bytes in the buffer and
            is not related to the available console space.
 
        .NOTES
            Name: Get-UsnJournalEntry
            Author: Boe Prox
            Version History
                1.0 //Boe Prox
                    -Initial Version
 
        .OUTPUT
            System.Journal.UsnEntry
 
        .EXAMPLE
            Get-UsnJournalEntry
 
            Description
            -----------
            Displays all journal entries starting from the beginning of the journal.
 
        .EXAMPLE
            Get-UsnJournalEntry -ReasonMask USN_REASON_RENAME_OLD_NAME, USN_REASON_RENAME_NEW_NAME, USN_REASON_FILE_DELETE |
            Select-Object -First 5 -Property Timestamp, USN, FileName, FullName, Reason
 
            Description
            -----------
            Displays all journal entries starting from the beginning of the journal that have had some sort of deletion. This highlights
            the fullname of each file that has been updated.
 
        .EXAMPLE
            Get-UsnJournalEntry -Tail -Wait
 
            Description
            -----------
            Starts watching the journal at the very end of the last entry and updates
            with new entries as the journal is updated.
    #>

    [OutputType('System.Journal.UsnEntry')]
    [cmdletbinding(
        DefaultParameterSetName = '__DefaultSetName'
    )]
    Param (
        [parameter()]
        [string]$DriveLetter = 'C:',
        [parameter()]
        [ValidateNotNullOrEmpty()]
        [USN_REASON]$ReasonMask,
        [parameter()]
        [int64]$StartUsn,
        [parameter(ParameterSetName='TailWait')]
        [switch]$Tail,
        [parameter(ParameterSetName='TailWait')]
        [switch]$Wait,
        [parameter(ParameterSetName='Paging')]
        [switch]$Paging
    )
    If ($PSBoundParameters.ContainsKey('Debug')){
        $DebugPreference = 'Continue'
    }
    $PSBoundParameters.GetEnumerator() | ForEach {
        Write-Verbose $_
    }
    If (-NOT $PSBoundParameters.ContainsKey('ReasonMask')) {
        $ReasonMask = [USN_REASON]([USN_REASON].GetEnumNames())
    }
    $VolumeHandle = OpenUSNJournal -DriveLetter $DriveLetter
    If ($VolumeHandle) {
        $JournalData = Get-USNJournal -VolumeHandle $VolumeHandle
    }
    If ($JournalData) {
        Write-Verbose 'Creating buffer'
        $DataSize = [System.Runtime.InteropServices.Marshal]::SizeOf([type][uint64]) * 0x4000
        $DataBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($DataSize)
        [void][PoshChJournal]::ZeroMemory($DataBuffer, $DataSize)
        $AvailableBytes = 0

        $ReadData = New-Object READ_USN_JOURNAL_DATA
        If ($PSBoundParameters.ContainsKey('StartUsn')) {
            $ReadData.StartUsn = $StartUsn
        } ElseIf ($PSBoundParameters.ContainsKey('Tail')) {
            $ReadData.StartUsn = $JournalData.NextUsn
        } Else {
            $ReadData.StartUsn = $JournalData.FirstUsn
        }
        Write-Debug "Starting USN: $($ReadData.StartUsn)"
        $ReadData.ReasonMask = $ReasonMask
        $ReadData.UsnJournalID = $JournalData.UsnJournalID
        $ReadDataSize = [System.Runtime.InteropServices.Marshal]::SizeOf($ReadData)
        $ReadBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ReadDataSize)
        [void][PoshChJournal]::ZeroMemory($ReadBuffer, $ReadDataSize)

        [System.Runtime.InteropServices.Marshal]::StructureToPtr($ReadData, $ReadBuffer, $True)
        $ReadMore = $True
        $Page = 0
        While ($ReadMore) {       
            $Page++
            $return = [PoshChJournal]::DeviceIoControl(
                $VolumeHandle,
                [EIOControlCode]::FSCTL_READ_USN_JOURNAL,
                $ReadBuffer,
                $ReadDataSize,
                $DataBuffer,
                $DataSize,
                [ref]$AvailableBytes,
                [intptr]::Zero
            )
            If ($return) {
                Write-Verbose "Processing USN entries"
                $Uint64Size = [System.Runtime.InteropServices.Marshal]::SizeOf([type][uint64])
                $UsnRecord =  New-Object intptr -ArgumentList ($DataBuffer.ToInt64() + $Uint64Size)
                Write-Debug "Initial Bytes: $($AvailableBytes)"
                While ($AvailableBytes -gt 60) {
                    $UsnEntry = NewUsnEntry -UsnRecord $UsnRecord -DriveLetter $DriveLetter -VolumeHandle $VolumeHandle
                    $UsnEntry.pstypenames.insert(0,'System.Journal.UsnEntry')
                    $UsnEntry
                    $UsnRecord =  New-Object IntPtr -ArgumentList ($UsnRecord.ToInt64() + $UsnEntry.RecordLength)
                    $AvailableBytes = $AvailableBytes - $UsnEntry.RecordLength
                    Write-Debug "Available Bytes: $($AvailableBytes)"
                }           
            } Else {
                Write-Warning 'Issue occurred reading Usn entries!'
                Break
            }            

            $NextUSN = [System.Runtime.InteropServices.Marshal]::ReadInt64($DataBuffer,0)
            Write-Debug "Next USN: $($NextUSN) - Journal Next USN: $($JournalData.NextUsn)"
            $NoMoreData = $NextUsn -ge $JournalData.NextUsn
            If (-NOT $NoMoreData -OR $PSBoundParameters.ContainsKey('Wait')) {
                If ($NoMoreData) {
                    Write-Verbose 'Checking for more data'
                    While ($NextUsn -ge $JournalData.NextUsn) {
                        Start-Sleep -Milliseconds 500
                        $JournalData = Get-USNJournal -VolumeHandle $VolumeHandle                    
                    }
                }
                If ($PSBoundParameters.ContainsKey('Paging')) {
                    While ($Choice -notmatch 'c|q') {
                        $Choice = Read-Host "Page: $($Page) - Press C to display next page or Q to Quit"
                    }
                }
                If ($Choice -eq 'c' -OR (-NOT $PSBoundParameters.ContainsKey('Paging'))) {
                    Write-Verbose "Using next Starting USN: $($NextUSN)"
                    [System.Runtime.InteropServices.Marshal]::WriteInt64($ReadBuffer, $NextUSN)
                    Remove-Variable Choice -ErrorAction SilentlyContinue
                } Else {
                    Write-Verbose "Halting operation"
                    $ReadMore = $False
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ReadBuffer)
                    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DataBuffer)                
                }
            } Else {
                $ReadMore = $False
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ReadBuffer)
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DataBuffer)
            }
        }
    }
}