diff --git a/HANDOFF.md b/HANDOFF.md index 1559da8..5b088ad 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -56,3 +56,22 @@ - Live USB 每次重启后,除了持久化分区内的数据,其他都会重置 - SSH server 需要每次重启后重新安装(除非持久化生效) - 持久化生效后,apt 安装的包会保留 + +## 2026-03-17 修复:persistence.dat 未格式化导致启动失败 + +### 问题 +- U 盘上的 `persistence.dat`(20GB)是空文件,没有 ext4 文件系统 +- `3-create-persistence.ps1` 因无可用标准 WSL,走了 Method B,只创建了稀疏空文件 +- Ventoy 启动 Ubuntu → 读 ventoy.json → 尝试挂载空的 persistence.dat → mount 失败 → 卡在 initramfs + +### 修复过程 +1. 用 docker-desktop WSL 的 `/sbin/mkfs.ext4` 格式化: + - 在 WSL 内创建稀疏文件:`dd if=/dev/zero of=/tmp/persistence.dat bs=1M count=0 seek=20480` + - 格式化:`/sbin/mkfs.ext4 -F -L casper-rw /tmp/persistence.dat` + - 复制到 C: → 移动到 E:(因为 docker-desktop WSL 无法直接访问 E: 盘) +2. 验证:offset 1080 处读到 `53 EF`(little-endian `0xEF53`),确认 ext4 有效 + +### 代码改进 +- `3-create-persistence.ps1` Method B 现在会尝试 docker-desktop WSL 格式化 +- 添加了 ext4 magic number 验证步骤 +- 只有在完全没有任何 WSL 时才降级为未格式化的空文件 diff --git a/bootable/3-create-persistence.ps1 b/bootable/3-create-persistence.ps1 index 9aa7883..0590bb3 100644 --- a/bootable/3-create-persistence.ps1 +++ b/bootable/3-create-persistence.ps1 @@ -93,12 +93,8 @@ if ($wslDistro) { } } else { - # ── Method B: Create raw file with PowerShell, format in Linux later ── - Write-Host "[INFO] No usable WSL distro (only docker-desktop found or none)." -ForegroundColor Yellow - Write-Host "" - Write-Host " Will create a raw persistence image file." -ForegroundColor White - Write-Host " It will be formatted automatically on first Linux boot." -ForegroundColor White - Write-Host "" + # ── 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)) { @@ -113,20 +109,67 @@ if ($wslDistro) { exit 1 } - Write-Host "[INFO] Creating ${sizeGB} GB sparse file (fast, only allocates on write)..." -ForegroundColor Yellow + $sizeMB = $sizeGB * 1024 + $formatted = $false - # Create sparse file using .NET (instant, doesn't write zeros) - $sizeBytes = [int64]$sizeGB * 1024 * 1024 * 1024 - $fs = [System.IO.File]::Create($PersistencePath) - $fs.SetLength($sizeBytes) - $fs.Close() + # 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 + wsl -d $dockerWSL -- dd if=/dev/zero of=$tmpFile bs=1M count=0 seek=$sizeMB 2>$null + wsl -d $dockerWSL -- /sbin/mkfs.ext4 -F -L casper-rw $tmpFile 2>$null + + # Copy out via /mnt/host/c/ (docker-desktop mounts C: there) + $tmpHostPath = "C:\uclaw_persistence_tmp.dat" + wsl -d $dockerWSL -- cp $tmpFile /mnt/host/c/uclaw_persistence_tmp.dat 2>$null + + if (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) { - Write-Host "[OK] Created ${sizeGB} GB persistence file." -ForegroundColor Green - Write-Host "" - Write-Host " IMPORTANT: After first boot into Linux, run this to format it:" -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 + $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" @@ -134,6 +177,26 @@ if ($wslDistro) { } } +# ── 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