Using QuickIO.Net with Powershell

This time around another .Net library called QuickIO.Net by the author Benjamin Abt.

The reason for this “project” was because a client of mine suffered from a Crypto Locker type event. It was caught quickly but not before some files out of those millions of files were encrypted.

The reason for using QuickIO.Net were threefold.

  1. Get-ChildItem don’t support paths of more than 260 characters.
  2. Conduct additional scripting/reporting after the list was created.
  3. Another workaround was in regard to using Robocopy and then RegEx to pull out the desired information. This produced issues with the file names when using any of the nordic special characters. E.g. åÅ, äÄ, öÖ.

The third point my not have been a big issue if it were one or two files but this was a scan of millions of files.

QuickIO.Net was very usefull as a workaround to the 260+ character limit.
I also thought this was a much cleaner solution compared to the Robocopy/RegEx solution.

The client also needed to check which files were changed at the time of the incident and only files with an extension of six characters long. The list could then be exported to a .csv file or used for further scripting e.g. move files to a quarantine location and so forth.

To use QuickIO.Net you need to download it from NuGet, a link is provided at the bottom of the page. Worth noting is that the QuickIO library can do much more than detailed in this post.

The input to the method that will search through the directories have three parameters.
The path to start from, any pattern filter using standard filters (e.g. “*.doc”) and if it should look through all the subdirectories or not.

The whole script is available via the links at the bottom of the page.
Here are the initial code and the first line that will load the .Net library.
You may want to change the path or even create a parameter for it if you want.

function Get-FilesQuickIO
{
    [CmdletBinding()]
    Param
    (
        # Path to start from
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$FilePath,

        # To search recursively
        [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
        [switch]$Recursive,

        # To filter
        [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Filter
    )

    # Load QuickIO Assmebly
    Add-Type -Path .\QuickIO\SchwabenCode.QuickIO.dll

I created some logic to handle the “Recursive” switch as the search option is named differently when calling the library. It also seems that if you use a filter and recursive at the same time the “EnumerateFiles” method will not do it recursively.
As a workaround I also created some logic to check if the pattern contains “*” or “*.*”.
If not then we first grab all the directories and then loop through those directories one by one.
The reason for not just using the directory loop and skip the extra logic, is that it takes about twice the time.
Actually it’s faster to get all the files and then use a pipe to “where” and filter it that way instead.
But for the sake of completeness I include the logic to handle the filter option.

    # Initiate the file list
    $fileList = @()

    # Set search option based on recursive or not
    if(($Recursive -eq $true) -and (($Filter -eq "*") -or ($Filter -eq "*.*")))
        {
        # Set the search option value
        $searchOption = "AllDirectories"

        # Get the list of files
        $fileList = [SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$FilePath,[system.string]$Filter,[System.IO.SearchOption]$searchOption)
        }
    elseif ($Recursive -eq $true)
        {
        # Set the search option value
        $searchOption = "AllDirectories"

        # Set the directory pattern
        $directoryPattern = "*"

        # Get all the directories as the recursive option doesn't work when using a filter.
        $directoryList = [SchwabenCode.QuickIO.QuickIODirectory]::EnumerateDirectories([system.string]$FilePath,[system.string]$directoryPattern,[System.IO.SearchOption]$searchOption) | select -ExpandProperty FullName

        # Loop through the directories with the file pattern
        foreach($d in $directoryList)
            {
            $fileList += try{[SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$d,[system.string]$Filter,[System.IO.SearchOption]$searchOption)}catch{$null}
            }
        }
    else{
        # Set the search option value
        $searchOption = "TopDirectoryOnly"

        # Get the list of files
        $fileList = try{[SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$FilePath,[system.string]$Filter,[System.IO.SearchOption]$searchOption)}catch{$null}
        }
    return $fileList

As you may have noticed in this example I’m not dealing with any exceptions.
Which you may/should do of course but as this is an example code I’ll leave that up to you to create.

Finaly to solve the six character extension check I created some code outside the function.
I could’ve included it in the code above or created another function for it but this was kind of a one off, hopefully.

    $result = @()    

    foreach($f in $fileList)
        {
        $extension = $f.Name.Split(".") | select -last 1
    
        if($extension -match "\b.{6}\b")
            {
            $result += $f
            }
        }

The result from QuickIO.Net will include the full path, file name, size, dates and so forth.
You can then easily use standard powershell filters like “Where” or “Select” to pull the information you want.
E.g. in the scenario described above we used a Where statement on the returned result to just select the files changed on that particular date.

Hope you found this usefull and again the library is capable of a lot more if you need it for other purposes.

Script source code
QuickIO.Net