Skip to content

Instantly share code, notes, and snippets.

@Hashbrown777
Last active December 3, 2023 20:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hashbrown777/35a2728c7f475775166399480e768029 to your computer and use it in GitHub Desktop.
Save Hashbrown777/35a2728c7f475775166399480e768029 to your computer and use it in GitHub Desktop.
Attach one or more physical disks to a wsl instance, using encryption and formatting options `wsl --mount` doesn't support
<# calling the wslMount function will attach one or more physical disks to a wsl instance
-drives
This parameter specifies which disks, and is an array of what Windows calls the Friendly Name.
You can see these by calling `Get-PhysicalDisk`.
-count
The list of friendly names are not necessarily unique (eg if you have multiple of the same model of drive).
Count is required so that you know how many disks have been matched, and will be passed to WSL.
-wsl
The name of the wsl install you want to interact with, defaults to the default wsl instance.
-mount
The path to which the disks will be mounted, the directory cannot already exist, but its parent must.
Non-absolute paths are relative to /mnt/wsl, and it is recommended to use a single folder name here, as
this is the only place where mounts will appear to the windows environment (via '\\wsl.localhost').
In the case you have provided -mapper, you may omit this, and it is just copied from that value.
-mapper
Specifies that the disks individually use luks 2 encryption prior to mounting.
The string passed here is used to name the mapper device.
-options
An array of strings that will be used as the -o for linux' `mount`
-format
A linux command to which the disk device paths are appended that should format the disks.
Eg `-format 'mkfs.btrfs -f'` may result in the command `mkfs.btrfs -f /dev/sdd /dev/sde /dev/sdf` being ran in wsl
Example:
#encrypt four disks, format them using btrfs, and raid them together, mounting at \\wsl$\Debian\mnt\wsl\data
wslMount `
-drives 'WDC WD20 EARS-00S8B1','ST2000DL 003-9VT166','ST2000DM 006-2DM164' `
-mapper 'data' `
-count 4 `
-format 'mkfs.btrfs -m raid1 -d raid0 -f'
#subsequent mounts omit the -format option, and we can decide to use some btrfs-specific mount options
wslMount `
-drives 'WDC WD20 EARS-00S8B1','ST2000DL 003-9VT166','ST2000DM 006-2DM164' `
-mapper 'data' `
-count 4 `
-options space_cache,compress
Read all outputs.
If you see an error, Ctrl+C at the next prompt, and fix the issue (and/or cleanup).
If it's something like a mistyped decryption key too many times letting it continue (failing to mount) will be fine, and actually perform cleanup for you (it'll fail to unmount, but will detach the disks from wsl for you).
But if formatting disks for the first time from something like say, NTFS, and an error said it was unable to attach one of the disks, definitely exit out (and in this case detach any disks from wsl and remove drive letters from windows mounted volumes).
#>
Function GetDisks { Param($matcher)
Get-PhysicalDisk `
| ?{ $_.FriendlyName -match "^$matcher`$" } `
| Sort-Object -Property SerialNumber `
| %{'\\.\PHYSICALDRIVE' + $_.DeviceId}
}
Function GetDevices { Param($wsl, $matcher)
"lsblk -o PATH,VENDOR,MODEL | grep -oP '^\S+(?=\s+$matcher\s*`$)' | sort" `
| %{
wsl `
--distribution $wsl `
-- bash -c $_
}
}
Function AttachDisks { Param($disks, $wsl)
#The mount command must be ran as your user, not admin.
#I have no idea how, but if you run the script as admin, you cannot see the mount, even if you log into wsl.
#Only the admin at that point can see the mount when they log into wsl, even if the same user!
#Thus only this portion should be elevated.
#
#Start-Process really fucks with stuff passed into argumentlist
#and the below hack was really the only way to do it.
Start-Process `
-Verb RunAs `
-Wait `
-FilePath powershell `
-ArgumentList `
@"
($(
($disks | %{ "'$_'" }) -join ','
)) | %{
'Attaching',`$_ -join ' '
wsl --distribution $wsl --mount `$_ --bare
}
pause
"@
}
Function ToMatcher { Param($names)
#[regex]::Escape() turns ' ' into '\ ' for some ungodly reason
"($((
$names `
| %{ <#[regex]::Escape#>($_) -replace '\s+','\s+' }
) -join '|'))"
}
Function DefaultWSL {
wsl -l `
| %{
#wsl returns strings in utf16 format, and powershell has no way of making it native
#pwsh works in utf16 natively, yet $_[0] or $_[1] will be a null char, so on and so forth for every second character
#bizarre
$_ = $_ -replace '\0',''
if ($_ -match '^(.*?)\s+\(Default\)$') {
$Matches[1]
}
}
}
Filter RootCommand { Param($wsl)
wsl `
--distribution $wsl `
--user root `
-- bash -c $_
}
Function wslMount { Param(
[Parameter(Mandatory=$true)][string[]]$drives,
[Parameter(Mandatory=$true)][int]$count,
[string]$wsl,
[string]$mount,
[string]$mapper,
[string[]]$options,
[string]$format
)
$opts = ''
if ($options) {
$opts = " -o $($options -join ',')"
}
$matcher = ToMatcher -names $drives
if (!$wsl) {
$wsl = DefaultWSL
}
if (!$mount) {
$mount = $mapper
if (!$mapper) {
throw 'Must use at least one of mount name or luks mapper identifier'
}
}
if ($mount -match '^[^/]') {
$mount = '/mnt/wsl/' + $mount
}
$disks = GetDisks -matcher $matcher
if ($disks.Count -ne $count) {
throw 'Mismatched disks'
}
$disks
"Attach drives to $wsl"
if ($mapper) {
"Using encryption via /dev/mapper/${mapper}X"
}
else {
'With no encryption'
}
if ($format) {
'THE DISKS WILL BE WIPED'
(@($format) + ('disk1','disk2','...')) -join ' '
}
"Mount it as $mount"
'','Proceed?'
pause
AttachDisks -wsl $wsl -disks $disks
$devices = GetDevices -wsl $wsl -matcher $matcher
if ($mapper) {
'',('Encrypt?','Decrypt?')[!$format]
pause
$devices `
| &{
Begin {
$index = -1
}
Process {
++$index
if ($format) {
"cryptsetup luksFormat --type luks2 '$_'"
}
"cryptsetup luksOpen '$_' '$mapper$index'"
}
} `
| RootCommand -wsl $wsl
$devices = 0..($count - 1) `
| %{
"/dev/mapper/$mapper$_"
}
}
if ($format) {
'','Format?'
pause
(@($format) + $devices) -join ' ' `
| RootCommand -wsl $wsl
}
'','Mount?'
pause
$devices `
| %{
"mkdir '$mount' 2>/dev/null && (mount$opts '$_' '$mount' || rmdir '$mount')"
} `
| RootCommand -wsl $wsl
explorer "\\wsl`$\$wsl$($mount -replace '/','\')"
'','Unmount?'
pause
"umount '$mount' && rmdir '$mount'" `
| RootCommand -wsl $wsl
'','Detach?'
pause
if ($mapper) {
$devices `
| %{
"cryptsetup luksClose '$_'"
} `
| RootCommand -wsl $wsl
}
$disks `
| %{
"Detaching $_"
wsl --unmount $_
}
'','Done!'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment