- 3-create-persistence.ps1: chain dd && mkfs.ext4 with && (was ;, so dd
failures still ran mkfs and produced bogus images); fix \wsl$\$wslDistro
variable expansion ambiguity using \${wslDistro}; mirror docker-desktop
branch with the same && chaining
- setup-openclaw.sh: guard mktemp failure; ensure partial node tarballs are
cleaned via EXIT trap; tolerate hosts with no non-loopback NIC under
pipefail; don't let test-installation.sh failure exit the post-install
- install.sh: add `set -o pipefail`; surface npm install failures (was
hidden by `| tail -5`); JSON-escape user-provided API_KEY before writing
to openclaw.json so keys with backslashes/quotes don't break the config
- Add SECURITY.md: private disclosure channel + scope
209 lines
8.6 KiB
PowerShell
209 lines
8.6 KiB
PowerShell
# ============================================================
|
|
# U-Claw Bootable USB - Step 3: Create Persistence Image
|
|
# Creates persistence.dat for Ventoy
|
|
# ============================================================
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Write-Host ""
|
|
Write-Host "============================================" -ForegroundColor Cyan
|
|
Write-Host " U-Claw Bootable USB - Step 3: Persistence" -ForegroundColor Cyan
|
|
Write-Host "============================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# ── Config ──
|
|
$CacheDir = Join-Path $PSScriptRoot ".download-cache"
|
|
$PersistencePath = Join-Path $CacheDir "persistence.dat"
|
|
$DefaultSizeGB = 20
|
|
|
|
if (-not (Test-Path $CacheDir)) {
|
|
New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null
|
|
}
|
|
|
|
# ── Check if persistence.dat already exists ──
|
|
if (Test-Path $PersistencePath) {
|
|
$existingSize = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
|
|
Write-Host "[INFO] persistence.dat already exists (${existingSize} GB)." -ForegroundColor Yellow
|
|
$overwrite = Read-Host "Recreate it? (y/N)"
|
|
if ($overwrite -ne "y" -and $overwrite -ne "Y") {
|
|
Write-Host "[OK] Using existing persistence.dat." -ForegroundColor Green
|
|
Write-Host " Next: Run .\4-copy-to-usb.ps1" -ForegroundColor Cyan
|
|
Read-Host "Press Enter to continue"
|
|
exit 0
|
|
}
|
|
Remove-Item -Path $PersistencePath -Force
|
|
}
|
|
|
|
# ── Detect usable WSL distro (must have bash + mkfs.ext4) ──
|
|
$wslDistro = $null
|
|
try {
|
|
$distros = (wsl --list --quiet 2>$null) -replace "`0","" | Where-Object { $_.Trim() -ne "" -and $_ -notmatch "docker" }
|
|
foreach ($d in $distros) {
|
|
$d = $d.Trim()
|
|
if ($d) {
|
|
$testResult = wsl -d $d -- sh -c "command -v mkfs.ext4 && echo HASEXT4" 2>$null
|
|
if ($testResult -match "HASEXT4") {
|
|
$wslDistro = $d
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} catch {}
|
|
|
|
if ($wslDistro) {
|
|
# ── Method A: Use WSL distro to create ext4 image ──
|
|
Write-Host "[INFO] Usable WSL distro found: $wslDistro" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
$sizeInput = Read-Host "Persistence size in GB (default: $DefaultSizeGB for 32GB USB)"
|
|
if ([string]::IsNullOrWhiteSpace($sizeInput)) {
|
|
$sizeGB = $DefaultSizeGB
|
|
} else {
|
|
$sizeGB = [int]$sizeInput
|
|
}
|
|
|
|
if ($sizeGB -lt 1 -or $sizeGB -gt 28) {
|
|
Write-Host "[ERROR] Size must be between 1 and 28 GB." -ForegroundColor Red
|
|
Read-Host "Press Enter to exit"
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "[INFO] Creating ${sizeGB} GB ext4 image via WSL..." -ForegroundColor Yellow
|
|
|
|
$sizeMB = $sizeGB * 1024
|
|
$tmpFile = "/tmp/uclaw_persistence.dat"
|
|
|
|
# Chain with && so a failed dd aborts before mkfs; capture marker on success only.
|
|
$wslOutput = wsl -d $wslDistro -- sh -c "rm -f $tmpFile && dd if=/dev/zero of=$tmpFile bs=1M count=0 seek=$sizeMB 2>/dev/null && mkfs.ext4 -F -L casper-rw $tmpFile >/dev/null 2>&1 && echo DONE"
|
|
if ($wslOutput -notmatch "DONE") {
|
|
Write-Host "[ERROR] WSL failed to create/format ext4 image (dd or mkfs.ext4 returned non-zero)." -ForegroundColor Red
|
|
wsl -d $wslDistro -- rm -f $tmpFile 2>$null
|
|
Read-Host "Press Enter to exit"
|
|
exit 1
|
|
}
|
|
|
|
# Copy out via \\wsl$ — use ${wslDistro} so PowerShell does not greedily extend the variable name into the path segment.
|
|
$wslNetPath = "\\wsl`$\${wslDistro}\tmp\uclaw_persistence.dat"
|
|
if (Test-Path $wslNetPath) {
|
|
Write-Host "[INFO] Copying from WSL to cache..." -ForegroundColor Yellow
|
|
Copy-Item -Path $wslNetPath -Destination $PersistencePath -Force
|
|
wsl -d $wslDistro -- rm -f $tmpFile
|
|
}
|
|
|
|
if (Test-Path $PersistencePath) {
|
|
$actualSize = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
|
|
Write-Host "[OK] Created ${actualSize} GB persistence image." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[ERROR] Failed to create persistence image via WSL." -ForegroundColor Red
|
|
Read-Host "Press Enter to exit"
|
|
exit 1
|
|
}
|
|
|
|
} else {
|
|
# ── Method B: Try docker-desktop WSL, fallback to raw file ──
|
|
Write-Host "[INFO] No standard WSL distro found." -ForegroundColor Yellow
|
|
|
|
$sizeInput = Read-Host "Persistence size in GB (default: $DefaultSizeGB for 32GB USB)"
|
|
if ([string]::IsNullOrWhiteSpace($sizeInput)) {
|
|
$sizeGB = $DefaultSizeGB
|
|
} else {
|
|
$sizeGB = [int]$sizeInput
|
|
}
|
|
|
|
if ($sizeGB -lt 1 -or $sizeGB -gt 28) {
|
|
Write-Host "[ERROR] Size must be between 1 and 28 GB." -ForegroundColor Red
|
|
Read-Host "Press Enter to exit"
|
|
exit 1
|
|
}
|
|
|
|
$sizeMB = $sizeGB * 1024
|
|
$formatted = $false
|
|
|
|
# Try docker-desktop WSL (has /sbin/mkfs.ext4)
|
|
$dockerWSL = $null
|
|
try {
|
|
$allDistros = (wsl --list --quiet 2>$null) -replace "`0","" | Where-Object { $_.Trim() -ne "" -and $_ -match "docker" }
|
|
foreach ($d in $allDistros) {
|
|
$d = $d.Trim()
|
|
if ($d) {
|
|
$testResult = wsl -d $d -- /sbin/mkfs.ext4 -V 2>&1
|
|
if ($LASTEXITCODE -eq 0 -or "$testResult" -match "mke2fs") {
|
|
$dockerWSL = $d
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} catch {}
|
|
|
|
if ($dockerWSL) {
|
|
Write-Host "[INFO] Found docker-desktop WSL with mkfs.ext4, creating ext4 image..." -ForegroundColor Green
|
|
$tmpFile = "/tmp/uclaw_persistence.dat"
|
|
|
|
# Create sparse file and format in docker-desktop WSL — chain with && so a dd failure aborts before mkfs/cp.
|
|
$dockerOutput = wsl -d $dockerWSL -- sh -c "rm -f $tmpFile && dd if=/dev/zero of=$tmpFile bs=1M count=0 seek=$sizeMB 2>/dev/null && /sbin/mkfs.ext4 -F -L casper-rw $tmpFile >/dev/null 2>&1 && cp $tmpFile /mnt/host/c/uclaw_persistence_tmp.dat && echo DONE"
|
|
|
|
$tmpHostPath = "C:\uclaw_persistence_tmp.dat"
|
|
if (($dockerOutput -match "DONE") -and (Test-Path $tmpHostPath)) {
|
|
Move-Item -Path $tmpHostPath -Destination $PersistencePath -Force
|
|
wsl -d $dockerWSL -- rm -f $tmpFile 2>$null
|
|
$formatted = $true
|
|
Write-Host "[OK] ext4 image created via docker-desktop WSL." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[WARN] docker-desktop copy failed, falling back to raw file." -ForegroundColor Yellow
|
|
wsl -d $dockerWSL -- rm -f $tmpFile 2>$null
|
|
}
|
|
}
|
|
|
|
if (-not $formatted) {
|
|
# Fallback: create raw sparse file
|
|
Write-Host "[INFO] Creating ${sizeGB} GB sparse file (needs manual format in Linux)..." -ForegroundColor Yellow
|
|
$sizeBytes = [int64]$sizeGB * 1024 * 1024 * 1024
|
|
$fs = [System.IO.File]::Create($PersistencePath)
|
|
$fs.SetLength($sizeBytes)
|
|
$fs.Close()
|
|
}
|
|
|
|
if (Test-Path $PersistencePath) {
|
|
$actualSize = [math]::Round((Get-Item $PersistencePath).Length / 1GB, 1)
|
|
Write-Host "[OK] Created ${actualSize} GB persistence file." -ForegroundColor Green
|
|
if (-not $formatted) {
|
|
Write-Host ""
|
|
Write-Host " IMPORTANT: File is NOT yet formatted as ext4!" -ForegroundColor Yellow
|
|
Write-Host " After first boot into Linux, run:" -ForegroundColor Yellow
|
|
Write-Host ' sudo mkfs.ext4 -F -L casper-rw /media/*/Ventoy/persistence.dat' -ForegroundColor White
|
|
Write-Host ' Then reboot for persistence to take effect.' -ForegroundColor White
|
|
}
|
|
} else {
|
|
Write-Host "[ERROR] Failed to create persistence file." -ForegroundColor Red
|
|
Read-Host "Press Enter to exit"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# ── Verify ext4 magic number ──
|
|
if (Test-Path $PersistencePath) {
|
|
try {
|
|
$f = [System.IO.File]::OpenRead($PersistencePath)
|
|
$f.Seek(1080, 0) | Out-Null
|
|
$buf = New-Object byte[] 2
|
|
$f.Read($buf, 0, 2) | Out-Null
|
|
$f.Close()
|
|
$magic = "{0:X2}{1:X2}" -f $buf[0], $buf[1]
|
|
if ($magic -eq "53EF") {
|
|
Write-Host "[OK] ext4 verified (magic: 0xEF53)." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[WARN] NOT a valid ext4 filesystem (magic: 0x$magic)." -ForegroundColor Yellow
|
|
Write-Host " You must format it in Linux before persistence will work." -ForegroundColor Yellow
|
|
}
|
|
} catch {
|
|
Write-Host "[WARN] Could not verify ext4 magic number." -ForegroundColor Yellow
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "[OK] Step 3 complete! Persistence image created." -ForegroundColor Green
|
|
Write-Host " Path: $PersistencePath" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host " Next: Run .\4-copy-to-usb.ps1" -ForegroundColor Cyan
|
|
Read-Host "Press Enter to continue"
|