Created
January 16, 2023 03:03
-
-
Save brad-jones/64fa6ae463548dbddafa7470d33b98a2 to your computer and use it in GitHub Desktop.
WSL Home Disk Auto Mount
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ARG USERNAME | |
ARG WSL_DISTRO_NAME | |
RUN mv /home /home-tpl | |
COPY ./mount-home.ts /usr/bin/mount-home.ts | |
RUN deno cache /usr/bin/mount-home.ts | |
RUN chmod +x /usr/bin/mount-home.ts | |
RUN sed -i "s/%USERNAME%/$(printf '%s\n' "$USERNAME" | sed -e 's/[\/&]/\\&/g')/g" /usr/bin/mount-home.ts | |
RUN sed -i "s/%WSL_DISTRO_NAME%/$(printf '%s\n' "$WSL_DISTRO_NAME" | sed -e 's/[\/&]/\\&/g')/g" /usr/bin/mount-home.ts | |
COPY ./mount-home.service /usr/lib/systemd/system/mount-home.service | |
RUN systemctl enable mount-home.service |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=Mount VHDX for /home | |
Before=local-fs-pre.target | |
[Service] | |
Type=oneshot | |
RemainAfterExit=yes | |
ExecStart=/usr/bin/deno run --cached-only -A /usr/bin/mount-home.ts | |
[Install] | |
WantedBy=multi-user.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S deno run --cached-only -A | |
import * as fs from "https://deno.land/[email protected]/fs/mod.ts#^"; | |
import { $, _, env, NonZeroExitCode } from "https://deno.land/x/[email protected]/mod.ts#^"; | |
import { z } from "https://deno.land/x/[email protected]/mod.ts#^"; | |
const USERNAME = "%USERNAME%"; // sed replaced in Dockerfile | |
const WSL_DISTRO_NAME = "%WSL_DISTRO_NAME%"; // sed replaced in Dockerfile | |
const USERPROFILE_WIN = `C:\\Users\\${USERNAME}`; | |
const USERPROFILE = `/mnt/c/Users/${USERNAME}`; | |
const VHDX_WIN = `${USERPROFILE_WIN}\\.wsl\\home.vhdx`; | |
const VHDX = `${USERPROFILE}/.wsl/home.vhdx`; | |
const VHDX_SIZE_GB = 50; | |
const wsl = `${USERPROFILE}/AppData/Local/Microsoft/WindowsApps/wsl.exe`; | |
const pwsh = `${USERPROFILE}/.scoop/apps/pwsh/current/pwsh.exe`; | |
console.log({ | |
USERNAME, | |
WSL_DISTRO_NAME, | |
VHDX, | |
VHDX_WIN, | |
VHDX_SIZE_GB, | |
wsl, | |
pwsh, | |
}); | |
const createDisk = async () => { | |
await _`${pwsh} -NoProfile -C ${`New-VHD -Path ${VHDX_WIN} -Dynamic -SizeBytes ${VHDX_SIZE_GB}GB`}`; | |
console.log(`mount-home | created brand new disk @ ${VHDX_WIN}`); | |
}; | |
const attachDisk = async (retry = 0) => { | |
try { | |
await env({ | |
WSL_UTF8: "1", | |
WSLENV: `${Deno.env.get("WSLENV") ?? ""}:WSL_UTF8`, | |
}, _`${wsl} -d ${WSL_DISTRO_NAME} --mount --vhd ${VHDX_WIN} --bare`); | |
} catch (e) { | |
if (e instanceof NonZeroExitCode) { | |
if (e.stderr.includes("WSL_E_USER_VHD_ALREADY_ATTACHED")) { | |
console.log( | |
`mount-home | ${VHDX_WIN} already attached to ${WSL_DISTRO_NAME}`, | |
); | |
return; | |
} | |
// Workaround for: https://github.com/microsoft/WSL/issues/8843 | |
// Only seems to happen intermittently for me, not sure what the trigger is. | |
if (e.stderr.includes("MZ: not found")) { | |
console.log(`mount-home | caught MZ error, retry: ${retry}`); | |
if (retry === 0 && await fs.exists("/usr/lib/binfmt.d/WSLInterop.conf")) { | |
const exitsing = await Deno.readTextFile("/usr/lib/binfmt.d/WSLInterop.conf"); | |
console.log(`mount-home | hmmm... interesting, /usr/lib/binfmt.d/WSLInterop.conf already exists?`); | |
console.log(`---`); | |
console.log(exitsing); | |
console.log(`---`); | |
} | |
// I actually think we will need to reboot in order to solve this issue | |
if (retry > 3) throw e; | |
// Write the file that supposedly needs to exist | |
await fs.ensureDir("/usr/lib/binfmt.d"); | |
await Deno.writeTextFile("/usr/lib/binfmt.d/WSLInterop.conf", ":WSLInterop:M::MZ::/init:PF"); | |
console.log(`mount-home | written /usr/lib/binfmt.d/WSLInterop.conf`); | |
console.log(`mount-home | retrying in 500ms...`); | |
await new Promise((r) => setTimeout(r, 500)); | |
await attachDisk(++retry); | |
} | |
} | |
throw e; | |
} | |
console.log(`mount-home | attached ${VHDX_WIN} to ${WSL_DISTRO_NAME}`); | |
}; | |
const getDeviceID = async () => { | |
const lsblkOutput = z.object({ | |
blockdevices: z.array(z.object({ | |
name: z.string(), | |
"maj:min": z.string(), | |
rm: z.boolean(), | |
size: z.string(), | |
ro: z.boolean(), | |
type: z.string(), | |
mountpoints: z.array(z.string().nullable()), | |
})), | |
}); | |
const device = lsblkOutput.parse(JSON.parse(await $(_`lsblk --json`))) | |
.blockdevices | |
.filter((_) => | |
_.mountpoints.length === 0 || | |
(_.mountpoints.length === 1 && _.mountpoints[0] === null) | |
) | |
.find((_) => _.size === `${VHDX_SIZE_GB}G`); | |
if (!device) { | |
throw new Error("mount-home | failed to find device id"); | |
} | |
const deviceID = `/dev/${device.name}`; | |
console.log(`mount-home | found device @ ${deviceID}`); | |
return deviceID; | |
}; | |
const fmtDisk = async (deviceID: string) => { | |
await _`mkfs.ext4 ${deviceID}`; | |
console.log(`mount-home | ${deviceID} formatted`); | |
}; | |
const mountDisk = async (deviceID: string) => { | |
await fs.ensureDir("/home"); | |
await _`mount ${deviceID} /home`; | |
console.log(`mount-home | ${deviceID} mounted to /home`); | |
}; | |
const setupDisk = async () => { | |
await _`cp -R /home-tpl/. /home`; | |
await _`chown -R ${`${USERNAME}:${USERNAME}`} /home/${USERNAME}`; | |
}; | |
if (await fs.exists(VHDX)) { | |
await attachDisk(); | |
const deviceID = await getDeviceID(); | |
await mountDisk(deviceID); | |
Deno.exit(0); | |
} | |
await createDisk(); | |
await attachDisk(); | |
const deviceID = await getDeviceID(); | |
await fmtDisk(deviceID); | |
await mountDisk(deviceID); | |
await setupDisk(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment