|
| 1 | +# enable-projfs-on-all-drives.ps1 |
| 2 | +# |
| 3 | +# Source of truth for the EnableProjFSOnAllDrives scheduled task body. |
| 4 | +# This script is NOT deployed to disk in the user-mode install model; |
| 5 | +# instead, build-task-xml.ps1 base64-encodes the contents and embeds |
| 6 | +# them in the task XML's <Exec><Arguments> as -EncodedCommand. The |
| 7 | +# task then runs as: powershell.exe -EncodedCommand <base64-of-this>. |
| 8 | +# |
| 9 | +# Runs as LocalSystem (configured by the scheduled task) so it has |
| 10 | +# SE_LOAD_DRIVER_PRIVILEGE for FilterAttach and HKLM write access |
| 11 | +# for the Dev Drive allowed-filters registry. |
| 12 | +# |
| 13 | +# Two invocation modes (selected by the task's triggers): |
| 14 | +# 1. AT_SYSTEM_START - no DriveLetter argument. Reconciles the Dev |
| 15 | +# Drive allow-list (machine-wide) and attaches prjflt to every |
| 16 | +# eligible NTFS/ReFS volume. FilterAttach is not persistent |
| 17 | +# across reboots, so this is required every boot. |
| 18 | +# 2. Event 1006 from Microsoft-Windows-Partition/Diagnostic - |
| 19 | +# DriveLetter argument is the drive of the newly-mounted volume. |
| 20 | +# Attaches prjflt to just that one drive. Avoids work on every |
| 21 | +# USB plug-in / VHD mount. |
| 22 | +# |
| 23 | +# Logs to %ProgramData%\GVFS\enable-projfs-on-all-drives.log |
| 24 | +# (HKLM-writable from SYSTEM, persistent across reboots). |
| 25 | +# |
| 26 | +# Idempotent everywhere: fltmc NameCollision is treated as success, |
| 27 | +# fsutil devdrv setFiltersAllowed is a no-op if already set. Safe to |
| 28 | +# run repeatedly. |
| 29 | + |
| 30 | +[CmdletBinding()] |
| 31 | +param( |
| 32 | + # If provided, only attempt to attach to this single drive letter. |
| 33 | + # Used by the volume-mount trigger to scope work narrowly. When |
| 34 | + # absent, all NTFS/ReFS volumes are processed (boot trigger path), |
| 35 | + # and the Dev Drive allow-list is also reconciled. |
| 36 | + [string]$DriveLetter |
| 37 | +) |
| 38 | + |
| 39 | +$ErrorActionPreference = 'Stop' |
| 40 | + |
| 41 | +$logDir = Join-Path $env:ProgramData 'GVFS' |
| 42 | +$logPath = Join-Path $logDir 'enable-projfs-on-all-drives.log' |
| 43 | +if (-not (Test-Path $logDir)) { |
| 44 | + New-Item -ItemType Directory -Path $logDir -Force | Out-Null |
| 45 | +} |
| 46 | + |
| 47 | +function Write-Log([string]$msg) { |
| 48 | + $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $msg" |
| 49 | + Add-Content -Path $logPath -Value $line -Encoding UTF8 |
| 50 | +} |
| 51 | + |
| 52 | +function Set-PrjFltDevDriveAllowed { |
| 53 | + # Dev Drives consult a machine-wide allow-list at mount time to |
| 54 | + # decide which minifilters may attach. Without PrjFlt in the list, |
| 55 | + # GVFS cannot work on Dev Drives even if we call FilterAttach. |
| 56 | + # Set unconditionally; fsutil is a no-op if already set. |
| 57 | + try { |
| 58 | + $out = (& fsutil.exe devdrv setFiltersAllowed PrjFlt 2>&1 | Out-String).Trim() |
| 59 | + if ($LASTEXITCODE -eq 0) { |
| 60 | + Write-Log "DevDrive allow-list: PrjFlt allowed (output: $out)" |
| 61 | + } |
| 62 | + else { |
| 63 | + # Non-fatal: on older Windows builds without Dev Drive |
| 64 | + # support, fsutil devdrv may fail. Log and continue. |
| 65 | + Write-Log "DevDrive allow-list: fsutil exit=$LASTEXITCODE (likely no Dev Drive support on this OS) output=$out" |
| 66 | + } |
| 67 | + } |
| 68 | + catch { |
| 69 | + Write-Log "DevDrive allow-list: exception (likely no Dev Drive support): $_" |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +function Add-PrjFltToVolume([string]$drive) { |
| 74 | + $output = (& fltmc.exe attach PrjFlt "${drive}:" 2>&1 | Out-String).Trim() |
| 75 | + $exit = $LASTEXITCODE |
| 76 | + # NameCollision is success-equivalent: filter is already attached. |
| 77 | + # Check the output BEFORE the exit code because fltmc returns exit |
| 78 | + # 1 for NameCollision (despite it being benign). |
| 79 | + if ($output -match 'instance already exists' -or |
| 80 | + $output -match 'instance name collision' -or |
| 81 | + $output -match '0x801f0012') { |
| 82 | + Write-Log "OK ${drive}: already attached (NameCollision)" |
| 83 | + return $true |
| 84 | + } |
| 85 | + if ($exit -ne 0) { |
| 86 | + Write-Log "FAIL ${drive}: exit=$exit output=$output" |
| 87 | + return $false |
| 88 | + } |
| 89 | + Write-Log "OK ${drive}: attached (output: $output)" |
| 90 | + return $true |
| 91 | +} |
| 92 | + |
| 93 | +try { |
| 94 | + Write-Log "===== enable-projfs-on-all-drives.ps1 starting (DriveLetter='$DriveLetter') =====" |
| 95 | + |
| 96 | + if ($DriveLetter) { |
| 97 | + # Single-volume mode (volume-mount trigger) |
| 98 | + $drive = $DriveLetter.TrimEnd(':').TrimEnd('\').ToUpperInvariant() |
| 99 | + if ($drive.Length -ne 1) { |
| 100 | + Write-Log "ERROR: invalid DriveLetter '$DriveLetter' (parsed='$drive')" |
| 101 | + exit 2 |
| 102 | + } |
| 103 | + $vol = Get-Volume -DriveLetter $drive -ErrorAction SilentlyContinue |
| 104 | + if (-not $vol) { |
| 105 | + Write-Log "SKIP ${drive}: volume not found" |
| 106 | + exit 0 |
| 107 | + } |
| 108 | + if ($vol.FileSystemType -notin @('NTFS', 'ReFS')) { |
| 109 | + Write-Log "SKIP ${drive}: filesystem=$($vol.FileSystemType) (not NTFS/ReFS)" |
| 110 | + exit 0 |
| 111 | + } |
| 112 | + Add-PrjFltToVolume $drive | Out-Null |
| 113 | + } |
| 114 | + else { |
| 115 | + # All-volumes mode (boot trigger). Reconcile both the Dev Drive |
| 116 | + # allow-list AND per-volume attachments. Cheap; idempotent. |
| 117 | + Set-PrjFltDevDriveAllowed |
| 118 | + $volumes = Get-Volume | |
| 119 | + Where-Object { |
| 120 | + $_.DriveLetter -and |
| 121 | + $_.FileSystemType -in @('NTFS', 'ReFS') |
| 122 | + } |
| 123 | + Write-Log "Found $(@($volumes).Count) eligible volume(s)" |
| 124 | + foreach ($v in $volumes) { |
| 125 | + Add-PrjFltToVolume ([string]$v.DriveLetter) | Out-Null |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + Write-Log "===== enable-projfs-on-all-drives.ps1 done =====" |
| 130 | +} |
| 131 | +catch { |
| 132 | + Write-Log "EXCEPTION: $_" |
| 133 | + Write-Log $_.ScriptStackTrace |
| 134 | + exit 3 |
| 135 | +} |
0 commit comments