Support/ExecutionCore/Eigenverft.Manifested.Package.ExecutionCore.Tar.ps1

<#
    Eigenverft.Manifested.Package.ExecutionCore.Tar
 
    Minimal TAR reader helpers. This file knows TAR records only; compression
    wrappers live elsewhere.
#>


function Read-TarHeaderTextField {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [byte[]]$Header,

        [Parameter(Mandatory = $true)]
        [int]$Offset,

        [Parameter(Mandatory = $true)]
        [int]$Length
    )

    $end = $Offset
    $limit = $Offset + $Length
    while ($end -lt $limit -and $Header[$end] -ne 0) {
        $end++
    }

    if ($end -le $Offset) {
        return ''
    }

    return ([System.Text.Encoding]::ASCII.GetString($Header, $Offset, ($end - $Offset))).Trim()
}

function ConvertFrom-TarHeaderOctalSize {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [byte[]]$Header
    )

    $sizeText = Read-TarHeaderTextField -Header $Header -Offset 124 -Length 12
    $sizeText = ($sizeText -replace "`0", '').Trim()
    if ([string]::IsNullOrWhiteSpace($sizeText)) {
        return [int64]0
    }

    try {
        return [Convert]::ToInt64($sizeText, 8)
    }
    catch {
        throw "TAR header contains an invalid octal size '$sizeText'."
    }
}

function Test-TarHeaderIsZeroBlock {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [byte[]]$Header
    )

    foreach ($value in $Header) {
        if ($value -ne 0) {
            return $false
        }
    }

    return $true
}

function Get-NormalizedTarEntryPath {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [string]$Path
    )

    $normalized = ([string]$Path) -replace '\\', '/'
    $normalized = $normalized.Trim()
    while ($normalized.StartsWith('./', [System.StringComparison]::Ordinal)) {
        $normalized = $normalized.Substring(2)
    }
    return $normalized.TrimStart('/')
}

function Read-TarArchiveEntryBytes {
<#
.SYNOPSIS
Reads one file entry from an uncompressed TAR stream without extracting it.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [System.IO.Stream]$Stream,

        [Parameter(Mandatory = $true)]
        [string]$EntryPath
    )

    if ([string]::IsNullOrWhiteSpace($EntryPath)) {
        throw 'A tar entry path is required.'
    }

    $targetEntryPath = Get-NormalizedTarEntryPath -Path $EntryPath
    while ($true) {
        $header = Read-ExactBytesFromStream -Stream $Stream -Length 512 -Context 'TAR header'
        if (Test-TarHeaderIsZeroBlock -Header $header) {
            break
        }

        $name = Read-TarHeaderTextField -Header $header -Offset 0 -Length 100
        $prefix = Read-TarHeaderTextField -Header $header -Offset 345 -Length 155
        $entryPath = if ([string]::IsNullOrWhiteSpace($prefix)) { $name } else { "$prefix/$name" }
        $entryPath = Get-NormalizedTarEntryPath -Path $entryPath
        $entrySize = ConvertFrom-TarHeaderOctalSize -Header $header
        $typeFlag = [char]$header[156]
        $paddingSize = (512 - ($entrySize % 512)) % 512

        if ([string]::Equals($entryPath, $targetEntryPath, [System.StringComparison]::Ordinal) -and
            ($typeFlag -eq [char]0 -or $typeFlag -eq '0')) {
            $content = Read-ExactBytesFromStream -Stream $Stream -Length $entrySize -Context "TAR entry '$targetEntryPath'"
            if ($paddingSize -gt 0) {
                Skip-BytesFromStream -Stream $Stream -Length $paddingSize -Context "padding for TAR entry '$targetEntryPath'"
            }
            return $content
        }

        $skipLength = $entrySize + $paddingSize
        if ($skipLength -gt 0) {
            Skip-BytesFromStream -Stream $Stream -Length $skipLength -Context "TAR entry '$entryPath'"
        }
    }

    throw "TAR archive does not contain entry '$targetEntryPath'."
}

function Read-TarArchiveEntryText {
<#
.SYNOPSIS
Reads one text file entry from an uncompressed TAR stream without extracting it.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [System.IO.Stream]$Stream,

        [Parameter(Mandatory = $true)]
        [string]$EntryPath,

        [System.Text.Encoding]$Encoding = [System.Text.Encoding]::UTF8
    )

    $bytes = Read-TarArchiveEntryBytes -Stream $Stream -EntryPath $EntryPath
    return $Encoding.GetString($bytes)
}