feat: add homelab topology documentation, infrastructure improvement plans, and management scripts
This commit is contained in:
93
scripts/repair_sd_card_windows.ps1
Normal file
93
scripts/repair_sd_card_windows.ps1
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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."
|
||||
Reference in New Issue
Block a user