Files
SnarfCode/scripts/repair_sd_card_windows.ps1

94 lines
4.4 KiB
PowerShell

# Run this script in an Administrator PowerShell prompt to inspect the Linux ext4
# superblock on Partition 2 and patch H:\cmdline.txt with 'fsck.repair=yes ro'.
$ErrorActionPreference = "Continue"
$part1 = Get-Partition -DriveLetter H -ErrorAction SilentlyContinue
if (-not $part1) {
Write-Error "Drive H: not found! Make sure the SD card / SSD is inserted and mounted at H:."
exit 1
}
$diskNum = $part1.DiskNumber
$disk = Get-Disk -Number $diskNum
$part2 = Get-Partition -DiskNumber $diskNum -PartitionNumber 2 -ErrorAction SilentlyContinue
$physDrive = "\\.\PhysicalDrive$diskNum"
Write-Host "=== 1. Disk & Partition Summary ===" -ForegroundColor Cyan
Write-Host ("Disk {0}: {1} ({2}, Bus={3}, Size={4:N2} GB)" -f $diskNum, $disk.FriendlyName, $disk.HealthStatus, $disk.BusType, ($disk.Size / 1GB))
if ($part2) {
Write-Host ("Partition 2 (Linux ext4): Offset={0}, Size={1:N2} GB" -f $part2.Offset, ($part2.Size / 1GB))
Write-Host "`n=== 2. Reading Partition 2 Ext4 Superblock (Read-Only) ===" -ForegroundColor Cyan
$pyCode = @"
import struct, datetime
p = r'$physDrive'
offset = $($part2.Offset)
with open(p, 'rb') as f:
mbr = f.read(512)
sig = struct.unpack('<I', mbr[0x1b8:0x1bc])[0]
f.seek(offset + 1024)
sb = f.read(1024)
magic, state, errors = struct.unpack('<HHH', sb[0x38:0x3e])
mnt_cnt, max_mnt = struct.unpack('<Hh', sb[0x34:0x38])
last_mnt, last_wrt = struct.unpack('<II', sb[0x2c:0x34])
last_chk = struct.unpack('<I', sb[0x40:0x44])[0]
incompat = struct.unpack('<I', sb[0x60:0x64])[0]
err_cnt = struct.unpack('<I', sb[0x194:0x198])[0]
first_err_time = struct.unpack('<I', sb[0x198:0x19c])[0]
last_err_time = struct.unpack('<I', sb[0x1c8:0x1cc])[0]
fmt = lambda ts: datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') if ts else 'Never'
print(f' MBR Disk Signature : {sig:08x} (PARTUUID={sig:08x}-02)')
print(f' Ext4 Magic : 0x{magic:04x} (Valid={magic == 0xef53})')
print(f' Filesystem State : {state} (1=Clean, 2=ERRORS_DETECTED, 4=Orphan_Recovery)')
print(f' Journal Dirty Bit : {bool(incompat & 0x4)} (INCOMPAT_RECOVER)')
print(f' Recorded FS Errors : {err_cnt} (First: {fmt(first_err_time)}, Last: {fmt(last_err_time)})')
print(f' Errors Behavior : {errors} (1=Continue, 2=Remount-RO, 3=Panic)')
print(f' Mount Count : {mnt_cnt} / {max_mnt}')
print(f' Last Mount Time : {fmt(last_mnt)}')
print(f' Last Write Time : {fmt(last_wrt)}')
print(f' Last Fsck Time : {fmt(last_chk)}')
"@
python -c $pyCode
}
Write-Host "`n=== 3. Verifying & Patching H:\cmdline.txt with 'fsck.repair=yes ro' ===" -ForegroundColor Cyan
$cmdlinePath = "H:\cmdline.txt"
if (Test-Path $cmdlinePath) {
$orig = (Get-Content $cmdlinePath -Raw).Trim()
Write-Host " Current H:\cmdline.txt: $orig"
$tokens = [System.Collections.Generic.List[string]]($orig -split '\s+')
$changed = $false
if ($tokens.Contains("rw")) {
$null = $tokens.Remove("rw")
$changed = $true
}
if (-not $tokens.Contains("fsck.repair=yes")) {
$idx = $tokens.FindIndex({ param($t) $t.StartsWith("rootfstype=") })
if ($idx -ge 0) { $tokens.Insert($idx + 1, "fsck.repair=yes") } else { $tokens.Add("fsck.repair=yes") }
$changed = $true
}
if (-not $tokens.Contains("fsck.mode=force")) {
$idx = $tokens.IndexOf("fsck.repair=yes")
if ($idx -ge 0) { $tokens.Insert($idx + 1, "fsck.mode=force") } else { $tokens.Add("fsck.mode=force") }
$changed = $true
}
if (-not $tokens.Contains("ro")) {
$idx = $tokens.IndexOf("rootwait")
if ($idx -ge 0) { $tokens.Insert($idx, "ro") } else { $tokens.Add("ro") }
$changed = $true
}
if ($changed) {
Copy-Item $cmdlinePath "H:\cmdline.txt.bak" -Force
$newCmdline = ($tokens -join " ") + "`n"
[System.IO.File]::WriteAllText($cmdlinePath, $newCmdline, (New-Object System.Text.UTF8Encoding($false)))
Write-Host " Patched H:\cmdline.txt: $($newCmdline.Trim())" -ForegroundColor Green
} else {
Write-Host " Verified H:\cmdline.txt already has 'fsck.repair=yes' and 'ro'." -ForegroundColor Green
}
}
Write-Host "`n=== Ready! Safe to eject H: and boot the Raspberry Pi. ===" -ForegroundColor Green
Write-Host "With 'fsck.repair=yes ro' in H:\cmdline.txt, the Pi kernel mounts / read-only first,"
Write-Host "runs 'fsck.ext4 -y' on Partition 2 automatically during boot, and then remounts / read-write."