param( [switch]$Elevated, [string]$ResultFile ) $ErrorActionPreference = 'Stop' $repo = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) function Test-IsAdministrator { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($identity) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function New-SmokeSource([string]$Kind) { switch ($Kind) { 'mft' { @' package main import ( "fmt" "io" "b612.me/wincmd/ntfs/mft" ) func main() { r, n, err := mft.GetMFTFileReader(`C:\`) if err != nil { panic(err) } defer r.Close() buf := make([]byte, 1024) got, err := io.ReadFull(r, buf) if err != nil && err != io.ErrUnexpectedEOF { panic(err) } fmt.Printf("mft_length=%d\n", n) fmt.Printf("mft_first_read=%d\n", got) fmt.Printf("mft_sig=%x\n", buf[:4]) } '@ } 'usn' { @' package main import ( "fmt" "os" "path/filepath" "syscall" "b612.me/wincmd/ntfs/usn" "b612.me/win32api" ) func main() { dir, err := os.MkdirTemp("", "wincmd-usn-admin-") if err != nil { panic(err) } defer os.RemoveAll(dir) path := filepath.Join(dir, "admin-usn.txt") if err := os.WriteFile(path, []byte("admin smoke"), 0600); err != nil { panic(err) } f, err := os.Open(path) if err != nil { panic(err) } defer f.Close() var info syscall.ByHandleFileInformation if err := syscall.GetFileInformationByHandle(syscall.Handle(f.Fd()), &info); err != nil { panic(err) } vol := filepath.VolumeName(path) + `\` vh, err := usn.CreateFile(`\\.\`+vol[:len(vol)-1], syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL) if err != nil { panic(err) } defer syscall.Close(vh) id := win32api.DWORDLONG(uint64(info.FileIndexHigh)<<32 | uint64(info.FileIndexLow)) fh, err := usn.OpenFileByIdWithfd(vh, id, syscall.O_RDONLY, win32api.FILE_ATTRIBUTE_NORMAL) if err != nil { panic(err) } defer syscall.Close(fh) var info2 syscall.ByHandleFileInformation if err := syscall.GetFileInformationByHandle(fh, &info2); err != nil { panic(err) } fmt.Printf("usn_id_ok=true\n") fmt.Printf("usn_idx_hi=%d\n", info2.FileIndexHigh) fmt.Printf("usn_idx_lo=%d\n", info2.FileIndexLow) } '@ } default { throw "unknown smoke source kind: $Kind" } } } function Invoke-GoSmoke([string]$Kind, [System.Collections.Generic.List[string]]$Lines) { $tmpGo = Join-Path $env:TEMP ("wincmd_ntfs_{0}_smoke.go" -f $Kind) try { Set-Content -Path $tmpGo -Value (New-SmokeSource $Kind) -Encoding utf8 $output = & go run $tmpGo 2>&1 | Out-String $Lines.Add(("{0}_smoke_ok=true" -f $Kind)) foreach ($line in ($output -split "`r?`n")) { if ($line -ne '') { $Lines.Add($line) } } } catch { $Lines.Add(("{0}_smoke_ok=false" -f $Kind)) $Lines.Add(("{0}_smoke_err={1}" -f $Kind, $_.Exception.Message)) throw } finally { Remove-Item $tmpGo -Force -ErrorAction SilentlyContinue } } function Write-Result([System.Collections.Generic.List[string]]$Lines, [string]$ResultFilePath) { if ($ResultFilePath) { Set-Content -Path $ResultFilePath -Value $Lines -Encoding utf8 } else { foreach ($line in $Lines) { Write-Output $line } } } if (-not $Elevated -and -not (Test-IsAdministrator)) { if (-not $ResultFile) { $ResultFile = Join-Path $env:TEMP 'wincmd_ntfs_admin_smoke_result.txt' } Remove-Item $ResultFile -Force -ErrorAction SilentlyContinue $process = Start-Process pwsh.exe -Verb RunAs -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File',$PSCommandPath,'-Elevated','-ResultFile',$ResultFile -PassThru $process.WaitForExit() if (Test-Path $ResultFile) { Get-Content $ResultFile -Encoding utf8 Remove-Item $ResultFile -Force -ErrorAction SilentlyContinue } else { Write-Output 'result_file_missing' } exit $process.ExitCode } Set-Location $repo $lines = New-Object 'System.Collections.Generic.List[string]' $lines.Add('admin=' + (Test-IsAdministrator)) $failed = $false try { & go test ./ntfs/mft -run '^$' *> $null $lines.Add('mft_pkg_ok=true') } catch { $failed = $true $lines.Add('mft_pkg_ok=false') $lines.Add('mft_pkg_err=' + $_.Exception.Message) } try { Invoke-GoSmoke -Kind 'mft' -Lines $lines } catch { $failed = $true } try { Invoke-GoSmoke -Kind 'usn' -Lines $lines } catch { $failed = $true } Write-Result -Lines $lines -ResultFilePath $ResultFile if ($failed) { exit 1 }