-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathWsl.ps1
4150 lines (3957 loc) · 209 KB
/
Wsl.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# .SYNOPSIS
#
# Utility script for Windows Subsystem for Linux (WSL).
#
# .DESCRIPTION
#
# Utility script for managing WSL distros, mainly version 2 (WSL2).
# Supports tab completion of distro names.
# Supports setting up VPNKit: A set of services providing customized network
# connectivity from a network device in the VM used to host WSL2 distros,
# via a unix socket and Windows named pipe, to a gateway process running on the
# host, routing traffic to the host network device. Docker Desktop with WSL2
# backend is using much of the same system, the core parts used here are actually
# taken from the Docker Desktop toolset. The problem with the Hyper-V based
# networking that is default in WSL, is that it can easily be problematic with
# VPN, and will be blocked by some firewall software etc.
#
# New-Distro/Remove-Distro:
# New-Distro downloads one of the Linux WSL2 distro images from Microsoft
# Store, performs custom install, which will register them with WSL2 but not add
# them to Windows (no shortcut, no launcher, not uninstall from Settings etc),
# and which will create the virtual disk file into a configurable location.
# Remove-Distro unregisters the WSL distro and deleting its hard disk file.
#
# Start-Distro:
# Start new session, same as executing wsl.exe with only optional distribution
# name as argument. Default is to start it within current PowerShell console,
# use parameter -NewWindow to start as new console window.
#
# Stop-Distro:
# Stops a single distro, executing wsl.exe --terminate <name>, or all distros
# as well as the common virtual machine, executing wsl.exe --shutdown, if
# parameter -All.
#
# New-VpnKit:
# Downloads and prepares a copy-install program directory on host, containing
# tools and configuration files for the VPNKit network setup, deployed into
# the distro by Intall-VpnKit.
# This includes:
# - wsl-vpnkit (Linux/WSL2 shell script) from https://github.com/albertony/wslkit/wsl-vpnkit (fork of https://github.com/sakai135/wsl-vpnkit)
# - wsl-vpnkit.exe (Windows executable) and vpnkit-tap-vsockd (Linux/WSL2 executable)
# from Docker Desktop for Windows: https://hub.docker.com/editions/community/docker-ce-desktop-windows/
# - npiperelay.exe (Windows executable) from https://github.com/albertony/npiperelay
# - resolv.conf and wsl.conf (Linux/WSL2 DNS configuration files) generated.
# For updating, just run the command again, it will replace existing files.
# There is no Uninstall command, but to uninstall just delete the program
# directory. Remember, though, that any WSL distros configured with VPNKit
# will have reference to this path!
#
# Intall-VpnKit/Unintall-VpnKit:
# Intall-VpnKit copies executables prepared by a previous New-VpnKit
# from host and into the WSL distribution, and then performs necessary
# configuration. This includes installing the required packages if on
# a distro supported by this script: Primarily the "socat" package (with
# dependencies such as libwrap0 and libssl1.1 if also missing), which is
# missing on most (all) distributions. Also iproute2 (for the ip command),
# sed and grep are needed and will be installed if missing, but these are
# included by default on most distributions - with Arch as a the single
# known exception. Assuming there is no network connectivity in WSL,
# the package file(s) are downloaded on host and installed from file in WSL.
# The socat package is required by the VPNKit tooling.
# Debian 9 and Ubuntu does 20.04 does not include socat, but this script
# supports installing it. Other WSL distro imagess have not been tested.
# For updating, just run the command again, it will replace existing files.
# Unintall-VpnKit reverts everyting that Intall-VpnKit, except
# the package installations (socat).
#
# Start-VpnKit:
# Starts the VPNKit script that enables the network pipe connection.
# Default is to start in a new console window, but can also be started
# within the existing session. The distro must have been configured
# with Intall-VpnKit first!
# Note: If running multiple WSL distros, then the VPNKit script can
# only run in one of them, but since all are effectively sharing a
# single virtual machine they will all be able to use the VPNKit
# networking. The one thing that should be notet is that the script
# will update the DNS server settings in /etc/resolv.conf only on
# the distro in which it is running. For other distros to use the
# Windows host, via the VPNKit gateway, for DNS resolving, the
# nameserver configuration in /etc/resolv.conf must already be set
# correctly (or manually updated). The resolv.conf file generated
# by New-VpnKit and copied into the distros by Intall-VpnKit
# has hard-coded the IP 192.168.67.1 (and just in case, also a free,
# public DNS service IP), which is taken from the current
# version of the wsl-vpnkit, and is the value it sets as the gateway
# address when starting VPNKit. So as long as you make sure to run
# New-VpnKit first, and then Intall-VpnKit for all distros,
# then all should be using the same networking as long as
# Start-VpnKit is executed on one of them. If you need to, you can
# change the resolv.conf manually, but remember that the instance
# you run Start-VpnKit in will temporarily replace it with its own
# version using only the VPNKit gateway, and if you run Intall-VpnKit
# again it will replace the existing /etc/resolv.conf permanently.
#
# Various utilities:
# Additional generic WSL utility functions, that are more or less
# convenience wrappers for specific wsl.exe command line modes
# and stored configuration in registry. There are a lot of options
# that can be set in WSL configuration file /etc/wsl.conf, but these
# are not managed here (except for options that can also be set in registry).
# Note that the when accessing the registry, we are accessing settings
# managed by the WslService (formerly known as LxssManager) service,
# which the wsl.exe command line utility interfaces with. Luckily all changes
# seem to be reflected immediately, no need to restart services etc to make
# wsl.exe in sync with changes we do "behind the scenes" in registry.
# There are also some utility functions for accessing the global
# WSL configuration options, which is stored on INI file name .wslconfig in
# the Windows user profile directory.
# Some of the functions are:
# Get-Distro
# Get-DistroImage
# Get-DefaultDistroVersion/Set-DefaultDistroVersion
# Get-DefaultDistro/Set-DefaultDistro
# Get-DistroDistributionName/Set-DistroDistributionName
# Get-DistroPackageName
# Get-DistroPath/Set-DistroPath
# Get-DistroDefaultUserId/Set-DistroDefaultUserId
# Get-DistroFlags/Set-DistroFlags
# Get-DistroInterop/Set-DistroInterop
# Get-DistroAutoMount/Set-DistroAutoMount
# Get-DistroVersion
# Get-DistroFileSystemVersion
# Rename-Distro
# Move-Distro
# Get-OptionProcessors/Set-OptionProcessors
# Get-OptionMemory/Set-OptionMemory
# Get-OptionSwap/Set-OptionSwap
# Get-OptionSwapFile/Set-OptionSwapFile
# Get-OptionVmIdleTimeout/Set-OptionVmIdleTimeout
# Get-OptionGuiApplications/Set-OptionGuiApplications
# Get-OptionNestedVirtualization/Set-OptionNestedVirtualization
# Get-OptionNetworkingMode/Set-OptionNetworkingMode
# Get-OptionFirewall/Set-OptionFirewall
# Get-OptionDnsTunneling/Set-OptionDnsTunneling
# Get-OptionLocalhostForwarding/Set-OptionLocalhostForwarding
# Get-OptionAutoProxy/Set-OptionAutoProxy
# Get-OptionAutoMemoryReclaim/Set-OptionAutoMemoryReclaim
# Get-OptionSparseVhd/Set-OptionSparseVhd
#
# Main script for the VPNKit part, and the main inspiration for the
# network related functionality, is based on the following repository:
# https://github.com/sakai135/wsl-vpnkit
#
# Background information on the pipe based networking used by VPNKit
# is available in Docker documentation:
# https://github.com/moby/vpnkit/blob/master/docs/ethernet.md#plumbing-inside-docker-for-windows
#
# Note:
# - Tested on Debian 9 (stretch), Debian 10 (buster) and Ubuntu 20.04 LTS (Focal Fossa)
# official WSL images from Microsoft Store, Alpine 3.13.1 official
# "Minimal root filesystem" distribution from alpinelinux.org,
# and Arch 2021.02.01 official "bootstrap" distribution.
# - If there are problems with process already running, named pipe already
# existing etc: Check if you have a wsl-vpnkit.exe process on the host computer
# and kill that, terminate the distro (wsl.exe --terminate) or
# shutdown entire WSL VM (wsl.exe --shutdown), and try again.
# - To be able to run the VPNKit utility, the WSL distro installation must have
# package 'socat' installed. If WSL does not have network connectivity, the
# package file (and any missing dependencies) must be donloaded on the host
# and then installed from file into the WSL distro. This script handles
# this process automatically, in the Install-VpnKit method.
# - The script installed in WSL will get a reference back to the program
# directory on host, in the default value of variable VPNKIT_PATH which must
# contain the path to wsl-vpnkit.exe. If this is moved then the script must be
# updated accordingly, or the VPNKIT_PATH variable must be overridden each
# time the script is executed.
# - The /etc/resolv.conf created by Intall-VpnKit will contain a hard coded
# IP that is assumed to be the VPNKit gateway, so if this IP changes in the
# wsl-vpnkit launcher script then resolv.conf should be updated accordingly.
# The wsl-vpnkit launcher script will actually temporarily replace resolv.conf
# with a version containing the real VPNKit gateway IP, but if running other
# distros they will not get this update and uses the stored /etc/resolv.conf,
# normally with content from Intall-VpnKit.
# - A previous version of the VPNKit script (wsl-vpnkit) had to be run in bash,
# so when installing on Alpine distribution, which does not include bash by
# default, it had to be installed in addition to the required socat package.
# This was changed 16 Feb 2021, so the script is now plain posix
# sh, busybox and Alpine compatible.
#
# .EXAMPLE
# mkdir C:\Wsl
# PS C:\Wsl> cd C:\Wsl
# PS C:\Wsl> New-Distro -Name Primary -Destination .\Distributions\Primary -Image debian-gnulinux -UserName me -SetDefault
# PS C:\Wsl> New-VpnKit -Destination .\VPNKit
# PS C:\Wsl> Install-VpnKit -Name Primary -ProgramDirectory .\VPNKit
# PS C:\Wsl> Start-VpnKit
# PS C:\Wsl> Start-Distro
#
[CmdletBinding()]
param
(
[pscredential] $GitHubCredential # GitHub API imposes heavy rate limiting which can be avoided by authentication with username and a Personal Access Token as password.
)
function Get-GitHubApiAuthenticationHeaders([pscredential]$Credential)
{
if ($Credential)
{
@{
Authorization = "Basic $([Convert]::ToBase64String([System.Text.Encoding]::Ascii.GetBytes("$($Credential.UserName):$($Credential.GetNetworkCredential().Password)")))"
}
}
else
{
@{}
}
}
# .SYNOPSIS
# Utility function function to pipe output from external commands into the verbose stream.
function Out-Verbose
{
[CmdletBinding()]
param([Parameter(ValueFromPipeline=$true)] $Message)
process { $Message | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Write-Verbose }
}
# .SYNOPSIS
# Utility function to create a new directory with an auto-generated unique name.
function New-TempDirectory
{
[CmdletBinding()]
param
(
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path
)
do { $TempDirectory = Join-Path $Path ([System.IO.Path]::GetRandomFileName()) } while (Test-Path -LiteralPath $TempDirectory)
New-Item -Path $TempDirectory -ItemType Directory | Select-Object -ExpandProperty FullName
}
# .SYNOPSIS
# Utility function go get valid, absolute path to a directory.
# .DESCRIPTION
# Will either throw if directory does not exist, or create it automatically.
function Get-Directory
{
[CmdletBinding()]
param
(
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path,
[switch] $Create
)
if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
if (Test-Path -LiteralPath $Path) { throw "Path exists but is not a directory: ${Path}" }
if (-not $Create) { throw "Path does not exist: ${Path}" }
(New-Item -Path $Path -ItemType Directory).FullName
} else {
(Resolve-Path -LiteralPath $Path).Path
}
}
# .SYNOPSIS
# Utility function to convert from regular filesystem path, absolute or relative,
# to "extended-length path".
# What it does is simply resolving to absolute path and adding prefix "\\?\",
# if not already present.
# .LINK
# Get-StandardizedPath
function Get-ExtendedLengthPath()
{
param([Parameter(Mandatory,ValueFromPipeline)][string]$Path)
if ($Path -and $Path.StartsWith("\\?\")) {
$Path
} else {
"\\?\$($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath(${Path}))"
}
}
# .SYNOPSIS
# Utility function to convert from (possibly) "extended-length path" to back to
# standardized path.
# .DESCRIPTION
# What it does is simply removing the prefix "\\?\", if present.
# Since the extended-length paths are required to be absolute, the resulting path
# should also be an absolute path.
# .LINK
# Get-ExtendedLengthPath
function Get-StandardizedPath
{
param([Parameter(Mandatory,ValueFromPipeline)][string]$Path)
if ($Path -and $Path.StartsWith("\\?\")) {
$Path.Substring(4)
} else {
$Path
}
}
# .SYNOPSIS
# Utility function to get the final download URI and file name behind a possibly redirected uri.
function Resolve-DownloadUri
{
param([Parameter(Mandatory,ValueFromPipeline)][uri]$Uri)
$Request = [System.Net.WebRequest]::Create($Uri)
$Response = $Request.GetResponse()
try {
$ResponseUri = $Response.ResponseUri
if ($ResponseUri.Segments[-1].EndsWith('/')) {
throw "The specified uri does not point to a file: ${ResponseUri}"
}
[pscustomobject]@{
Uri = $ResponseUri.AbsoluteUri
FileName = $ResponseUri.Segments[-1]
}
} finally {
$Response.Close()
$Response.Dispose()
}
}
# .SYNOPSIS
# Utility function to download a single file from a specified URL to a specified location.
function Save-File
{
[CmdletBinding()]
param
(
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Url,
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path,
[pscredential] $Credential
)
if ($Credential) {
Write-Verbose "Downloading ${Url} (authenticated as $($Credential.UserName)) -> ${Path}"
#$GitHubApiHeaders = Get-GitHubApiAuthenticationHeaders -Credential $GitHubCredential
#Invoke-WebRequest -Uri $Url -UseBasicParsing -DisableKeepAlive -OutFile $Path -Headers $GitHubApiHeaders
Start-BitsTransfer -Source $Url -Destination $Path -Authentication Basic -Credential $Credential
#$WebClient = New-Object System.Net.WebClient
#$WebClient.Headers['Authorization'] = $GitHubApiHeaders['Authorization']
#$WebClient.Credential = $GitHubCredential
#$WebClient.DownloadFile($Url, $Path)
#$WebClient.Dispose()
} else {
Write-Verbose "Downloading ${Url} -> ${Path}"
Start-BitsTransfer -Source $Url -Destination $Path
}
# BitsTransfer may have problems with signed url downloads from GitHub,
# and also needs TLS 1.1/1.2 to be explicitely enabled on WinHTTP in Windows 7,
# so then WebClient can be used instead.
# Note: WebClient needs destination directory to exists, not BitsTransfer does not.
#$WebClient = New-Object System.Net.WebClient
#$WebClient.DownloadFile($Url, $Path)
#$WebClient.Dispose()
}
# .SYNOPSIS
# Utility function to install a single file from a temporary download location to the final destination.
# Checks for existing file, uses checksum to decide if identical and if it needs to be replaced.
# NOTE: The source file will be moved into destination or deleted!
function Install-File
{
[CmdletBinding()]
param
(
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $FileName,
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $SourceDirectory,
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $DestinationDirectory,
[switch] $CheckRunningProcess,
[switch] $Force
)
$SourceFullName = Join-Path $SourceDirectory $FileName
$DestinationFullName = Join-Path $DestinationDirectory $FileName
Write-Verbose "Installing '${FileName}' to '${DestinationDirectory}'"
if (Test-Path -LiteralPath $DestinationFullName) {
Write-Verbose "Already exists: ${DestinationFullName}"
$ExistingHash = Get-FileHash -LiteralPath $DestinationFullName | Select-Object -ExpandProperty Hash
Write-Verbose "Checksum existing: `"${ExistingHash}`""
$DownloadHash = Get-FileHash -LiteralPath $SourceFullName | Select-Object -ExpandProperty Hash
Write-Verbose "Checksum download: `"${DownloadHash}`""
if ($Force -or $DownloadHash -ne $ExistingHash) {
if ($DownloadHash -ne $ExistingHash) {
Write-Host "Replacing different existing file '${FileName}'"
} else {
Write-Host "Replacing identical existing file '${FileName}' due to -Force option"
}
# Move executables into destination, replace any existing (due to -Force).
if ($CheckRunningProcess) {
# But if win executable is currently running the move will fail unless we kill it ifrst
$DestinationProcesses = Get-Process | Where-Object -Property Path -eq $DestinationFullName
if ($DestinationProcesses) {
if ($Force -or $PSCmdlet.ShouldContinue("The executable '${FileName}' in destination is currently running.`nIt must be stopped to be able to overwrite it.`nDo you want to stop it now?", "Stop ${FileName}")) {
$DestinationProcesses | Stop-Process -Force:$Force # If -Force then stop without prompting for confirmation (default is to prompt before stopping any process that is not owned by the current user)
} # else: Just try anyway, with a probable error being the result?
}
}
Move-Item -Force -LiteralPath $SourceFullName -Destination $DestinationDirectory
$true # Return true to indicate the file was installed
} else {
Write-Host "Keeping identical existing file '${FileName}'"
Remove-Item -LiteralPath $SourceFullName
$false # Return false to indicate the file was not installed
}
} else {
Write-Verbose "Installing new file '${FileName}'"
Move-Item -Force -LiteralPath $SourceFullName -Destination $DestinationDirectory
$true # Return true to indicate the file was installed
}
}
# .SYNOPSIS
# Utility function to get path to 7-Zip utility, downloading it if necessary.
function Get-SevenZip
{
[CmdletBinding()]
param
(
# The directory path where 7-Zip (7z.exe and 7z.dll) will be downloaded into, if necessary.
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $DownloadDirectory,
# Optional working directory where downloads will be temporarily placed. Default is current directory.
[Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string] $WorkingDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
)
$DownloadDirectory = Get-Directory -Path $DownloadDirectory -Create
$SevenZipPath = Join-Path $DownloadDirectory '7z.exe'
if (Test-Path -PathType Leaf $SevenZipPath) {
Write-Verbose "7-Zip already exists: $SevenZipPath"
} else {
$TempDirectory = New-TempDirectory -Path $WorkingDirectory
try
{
# Need the full (x64) edition to be able to extract installers etc.
# Picking version number from the url path, e.g. "21.03" from /7-Zip/21.03/7z2103-x64.exe,
# sorting as string assuming all but the first part is zero-filled to fixed two digit length.
$ReleaseInfo = Invoke-RestMethod -Uri "https://sourceforge.net/projects/sevenzip/rss?path=/7-Zip" -DisableKeepAlive | Where-Object { $_.title.'#cdata-section' -match "(?:/7-Zip/)(\d+(\.\d+)*)(?:/7z\d+-x64.exe)" } | Select-Object -Property @{Name = 'title'; Expression={$_.title.'#cdata-section'}}, pubDate, content, @{Name = 'version'; Expression={$Matches[1]}} | Sort-Object -Property version -Descending | Select-Object -First 1
if (-not $ReleaseInfo) { throw "Failed to find release info for 7-Zip" }
# Download the basic edition if necessary, either because it is the requested edition or because it is needed to extract a more advanced edition later.
# The full edition download is itself an archive, but its 7z/LZMA so we just need the simplest single-binary "7-Zip Reduced"
# which there is a direct download link for, so we download it temporarily if not finding something else.
# We need some 7-Zip utility to extract the download, see if there is one already, if not we will have to download the basic reduced version later
$SevenZipUtilPath = Get-Command -CommandType Application "7z.exe", ".\7z.exe", "7za.exe", ".\7za.exe", "7zr.exe", ".\7zr.exe", "7zdec.exe", ".\7zdec.exe" -ErrorAction Ignore | Select-Object -First 1 -ExpandProperty Source
if ($SevenZipUtilPath) {
Write-Verbose "Using existing 7-Zip utility for extracting full edition archive download: ${SevenZipUtilPath}"
} else {
Write-Verbose "Downloading latest version of 7-Zip Basic edition to be able to extract full edition archive download"
$DownloadName = '7zr.exe' # Single-binary reduced version, 7zr.exe, which can extract .7z and .lzma files only
$DownloadUrl = "https://www.7-zip.org/a/${DownloadName}"
$DownloadFullName = Join-Path $TempDirectory $DownloadName
Save-File -Url $DownloadUrl -Path $DownloadFullName
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Download of 7-Zip Basic, required to extract full edition archive download, seems to have failed, cannot find downloaded file ${DownloadBasicFullName}" }
$SevenZipUtilPath = $DownloadFullName
}
# Download the full edition
Write-Verbose "Downloading latest version of 7-Zip Full edition"
$DownloadName = $ReleaseInfo.title.Split("/")[-1]
$DownloadUrl = "https://www.7-zip.org/a/${DownloadName}"
$DownloadFullName = Join-Path $TempDirectory $DownloadName
Save-File -Url $DownloadUrl -Path $DownloadFullName
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Download of 7-Zip Full edition seems to have failed, cannot find downloaded file ${DownloadFullName}" }
# Verify hash
$ExpectedHash = $ReleaseInfo.content.hash.'#text'
$ExpectedHashAlgorithm = $ReleaseInfo.content.hash.algo
$ActualHash = (Get-FileHash -Algorithm $ExpectedHashAlgorithm $DownloadFullName).Hash
if ($ActualHash -ne $ExpectedHash) { throw "Checksum mismatch in downloaded 7-Zip Full edition archive ${DownloadFullName}: Expected ${ExpectedHash}, but was ${ActualHash}" }
Write-Verbose "Checksum successfully verified: ${ExpectedHash}"
# Extract (using the existing version, possibly the downloaded single-binary reduced version, 7zr.exe, which can extract .7z and .lzma files only)
&$SevenZipUtilPath e -y "-o${DownloadDirectory}" "${DownloadFullName}" '7z.exe' '7z.dll' | Out-Verbose
Remove-Item -LiteralPath $DownloadFullName
if ($LastExitCode -ne 0) { throw "Extraction of downloaded 7-Zip Full edition archive ${DownloadFullName} failed with error $LastExitCode" }
if (-not (Test-Path -PathType Leaf $SevenZipPath)) { throw "Cannot find extracted 7-Zip Full edition executable $SevenZipPath" }
}
finally
{
if ($TempDirectory -and (Test-Path -LiteralPath $TempDirectory)) {
Remove-Item -LiteralPath $TempDirectory -Recurse
}
}
}
$SevenZipPath
}
# .SYNOPSIS
# Utility function to get path to OpenSSL utility, downloading it if necessary.
function Get-OpenSsl
{
[CmdletBinding()]
param
(
# The directory path where OpenSSL (openssl.exe, libssl-*.dll and libcrypto-*.dll) will be downloaded into, if necessary.
[Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $DownloadDirectory,
# Optional working directory where downloads will be temporarily placed. Default is current directory.
[Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string] $WorkingDirectory = (Get-Location -PSProvider FileSystem).ProviderPath,
# Optional search path to 7-Zip utility. It is required for extracting downloads,
# but will be temporarily downloaded if not found.
[string[]] $SevenZip = ("7z", ".\7z")
)
$DownloadDirectory = Get-Directory -Path $DownloadDirectory -Create
$ApplicationExe = "openssl.exe"
$OpenSslPath = Join-Path $DownloadDirectory $ApplicationExe
if (Test-Path -PathType Leaf $OpenSslPath) {
Write-Verbose "OpenSSL already exists: $OpenSslPath"
} else {
$TempDirectory = New-TempDirectory -Path $WorkingDirectory
try
{
# Make sure we have required 7-Zip utility available, download into temp directory if necessary (will be deleted at the end when deleting entire temp directory)
$SevenZipPath = Get-Command -CommandType Application -Name $SevenZip -ErrorAction Ignore | Select-Object -First 1 -ExpandProperty Source
if (-not $SevenZipPath) {
Write-Host "Downloading required 7-Zip utility into temporary directory (use parameter -SevenZip to avoid)..."
$SevenZipPath = Get-SevenZip -DownloadDirectory $TempDirectory -WorkingDirectory $TempDirectory # Will create new tempfolder as subfolder of current $TempDirectory
}
# Get OpenSSH release information from official conda-forge repository
$RepositoryUrl = "https://conda.anaconda.org/conda-forge/win-64"
Write-Verbose "Downloading conda repository"
$DownloadName = "repodata.json.bz2"
$DownloadUrl = "${RepositoryUrl}/${DownloadName}"
$DownloadFullName = Join-Path $TempDirectory $DownloadName
Save-File -Url $DownloadUrl -Path $DownloadFullName
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Download of conda repository seems to have failed, cannot find downloaded file ${DownloadFullName}" }
&$SevenZipPath x "-o${TempDirectory}" $DownloadFullName | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Write-Verbose
Remove-Item -LiteralPath $DownloadFullName # Delete the repodata.json.bz2 just as soon as we're done with it (though, will delete entire temp dir later)
$DownloadFullName = Join-Path $TempDirectory ([System.IO.Path]::GetFileNameWithoutExtension($DownloadName))
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Extraction of conda repository archive seems to have failed, cannot find extracted file ${DownloadFullName}" }
$ReleaseInfo = (Get-Content -Raw $DownloadFullName | ConvertFrom-Json | Select-Object -ExpandProperty packages | Select-Object -Property "openssl*").PSObject | Select-Object -ExpandProperty Properties | Where-Object { $_.Value.version -as [version] } | ForEach-Object { @{ Name = $_.Name; Version = [version]$_.Value.version; BuildNumber = $_.Value.build_number; Build = $_.Value.build; Timestamp = [datetime]::new(1970,1,1,0,0,0,[System.DateTimeKind]::Utc).AddMilliseconds($_.Value.timestamp); Hash = $_.Value.sha256 } } | Sort-Object -Property @{Expression={$_.Version}; Descending=$true}, @{Expression={$_.BuildNumber}; Descending=$true}, @{Expression={$_.Timestamp}; Descending=$true} | Select-Object -First 1
Remove-Item -LiteralPath $DownloadFullName # Delete the repodata.json just as soon as we're done with it (though, will delete entire temp dir later)
if (-not $ReleaseInfo) { throw "Failed to find release info for OpenSSL" }
# Download latest release as compressed archive
Write-Verbose "Downloading from conda repository version $($ReleaseInfo.Version) (build $($ReleaseInfo.BuildNumber) uploaded $($ReleaseInfo.Timestamp))"
$DownloadName = $ReleaseInfo.Name
$DownloadUrl = "${RepositoryUrl}/${DownloadName}"
$DownloadFullName = Join-Path $TempDirectory $DownloadName
Save-File -Url $DownloadUrl -Path $DownloadFullName
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Download of OpenSSL seems to have failed, cannot find downloaded file ${DownloadFullName}" }
# Verify hash
$ExpectedHash = $ReleaseInfo.Hash
$ExpectedHashAlgorithm = "SHA256"
$ActualHash = (Get-FileHash -Algorithm $ExpectedHashAlgorithm $DownloadFullName).Hash
if ($ActualHash -ne $ExpectedHash) { throw "Checksum mismatch in downloaded OpenSSL archive ${DownloadFullName}: Expected ${ExpectedHash}, but was ${ActualHash}" }
Write-Verbose "Checksum successfully verified: ${ExpectedHash}"
# Extract .tar from .tar.bz2 compressed archive
Write-Verbose "Extracting .tar from .tar.bz2"
&$SevenZipPath x -y "-o${TempDirectory}" "${DownloadFullName}" | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Write-Verbose
Remove-Item -LiteralPath $DownloadFullName -ErrorAction Ignore # Delete the .tar.bz2
$DownloadFullName = Join-Path $TempDirectory ([System.IO.Path]::GetFileNameWithoutExtension($DownloadName))
if (-not (Test-Path -PathType Leaf $DownloadFullName)) { throw "Cannot find extracted OpenSSL archive $DownloadFullName" }
# Extract openssl.exe, libcrypt-*.dll and libssl-*.dll from the .tar archive (ignore everything else)
Write-Verbose "Extracting program files from .tar"
&$SevenZipPath e -y "-o${DownloadDirectory}" "${DownloadFullName}" "Library\bin\openssl.exe" "Library\bin\libssl-*.dll" "Library\bin\libcrypto-*.dll" | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.Exception.Message } else { $_ } } | Write-Verbose
Remove-Item -LiteralPath $DownloadFullName # Deletes the .tar
if ($LastExitCode -ne 0) { throw "Extraction of OpenSSL archive failed with error $LastExitCode" }
if (-not (Test-Path -PathType Leaf $SevenZipPath)) { throw "Cannot find extracted OpenSSL executable $SevenZipPath" }
}
finally
{
if ($TempDirectory -and (Test-Path -LiteralPath $TempDirectory)) {
Remove-Item -LiteralPath $TempDirectory -Recurse
}
}
}
$OpenSslPath
}
# .SYNOPSIS
# Helper function for optionally specifying the distro options: --distribution and --user.
function GetWslCommandDistroOptions([string] $Name, [string] $UserName)
{
$Options = @()
if ($Name) {
if (-not (Test-Distro $Name)) { throw "There is no WSL distro with name '${Name}'" }
$Options += '--distribution', $Name
} # else: No option means let wsl use default
if ($UserName) {
$Options += '--user', $UserName
}
$Options
}
# .SYNOPSIS
# Helper function to get argument completion.
# .DESCRIPTION
# Enable for a parameter accepting name of distro by prepending with:
# [ArgumentCompleter({CompleteDistroName @args})]
# .LINK
# ValidateDistroName
function CompleteDistroName
{
param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
Get-Distro | Where-Object { -not $WordToComplete -or ($_ -like $WordToComplete.Trim('"''') -or $_.ToLower().StartsWith($WordToComplete.Trim('"''').ToLower())) } | ForEach-Object { if ($_.IndexOfAny(" `"") -ge 0){"`'${_}`'"}else{$_} }
}
# .SYNOPSIS
# Helper function to validate value of parameter accepting name of distro.
# .DESCRIPTION
# Can be used in combination with argument completion with CompleteDistroName.
# Enable by prepending parameter with:
# [ValidateScript({ValidateDistroName $_})]
# .LINK
# CompleteDistroName
function ValidateDistroName
{
param ([string] $Name, [switch] $Physical)
if ($Name) {
$ValidSet = Get-Distro
if ($Name -notin $ValidSet) { throw [System.Management.Automation.PSArgumentException] "The value `"${_}`" is not a known image. Only `"$($ValidSet -join '", "')`" can be specified." }
}
$true
}
# .SYNOPSIS
# Helper function to validate value of parameter accepting distro user credential.
# .DESCRIPTION
# Enable by prepending parameter with:
# [ValidateScript({ValidateDistroUserName $_})]
function ValidateDistroUserName
{
param ([string] $UserName)
if ($UserName -notmatch '^[a-z_][a-z0-9_-]*[$]?$') { # Not strict requirement in all distros, but highly recommended to only use usernames that begin with a lower case letter or an underscore, followed by lower case letters, digits, underscores, or dashes. They can end with a dollar sign.
throw [System.Management.Automation.PSArgumentException] "The user name `"${UserName}`" is not valid. Must begin with a lower case letter or an underscore, followed by lower case letters, digits, underscores, or dashes. May end with a dollar sign."
}
$true
}
# .SYNOPSIS
# Helper function to get the primary registry key.
function GetWSLRegistryKey()
{
Get-Item -LiteralPath HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\
}
# .SYNOPSIS
# Helper function to get registry "item" for a specific distro or all distros.
# .DESCRIPTION
# The returned type is (list of) PSCustomObject, containing properties for all
# registry values, and "context" properties PSPath, PSParentPath and PSChildName
# with reference to the registry location.
function GetDistroRegistryItem([string] $Name, [switch] $All)
{
if ($All) {
GetWSLRegistryKey | Get-ChildItem | Get-ItemProperty
} else {
$Items = GetWSLRegistryKey | Get-ChildItem | Get-ItemProperty | Where-Object -Property DistributionName -eq (GetDistroNameOrDefault -Name $Name)
if ($Items.Count -gt 1) { throw "More than one distro with name '${Name}' found in registry" }
$Items
}
}
# .SYNOPSIS
# Get default version that new WSL distros will be created with.
# .LINK
# Set-DefaultDistroVersion
function Get-DefaultDistroVersion()
{
GetWSLRegistryKey | Get-ItemPropertyValue -Name DefaultVersion
}
# .SYNOPSIS
# Set default version that new WSL distros will be created with.
# .DESCRIPTION
# The same can be done with `wsl.exe --set-default-version <Version>`.
# .LINK
# Get-DefaultDistroVersion
function Set-DefaultDistroVersion
{
[CmdletBinding()] param([ValidateRange(1, 2)][Parameter(Mandatory)] [int] $Version)
GetWSLRegistryKey | Set-ItemProperty -Name DefaultVersion -Value $Version
}
# .SYNOPSIS
# Get default version that new WSL distros will be created with.
# .LINK
# Set-DefaultDistroVersion
function Get-NatIpAddress()
{
GetWSLRegistryKey | Get-ItemPropertyValue -Name NatIpAddress
}
# .SYNOPSIS
# Set default version that new WSL distros will be created with.
# .DESCRIPTION
# The same can be done with `wsl.exe --set-default-version <Version>`.
# .LINK
# Get-DefaultDistroVersion
function Set-NatIpAddress
{
[CmdletBinding()] param([ValidateRange(1, 2)][Parameter(Mandatory)] [ipaddress] $Ip)
GetWSLRegistryKey | Set-ItemProperty -Name NatIpAddress -Value $Ip
}
# .SYNOPSIS
# Get the name of the installed distro that is currently defined as default in WSL.
# .DESCRIPTION
# This is the distro that will be used by other functions when a specific distro is
# not specified, and also by the standard `wsl.exe` command line utility when not
# specifying a distro with argument --distribution or -d.
# .LINK
# Set-DefaultDistro
function Get-DefaultDistro()
{
$Guid = GetWSLRegistryKey | Get-ItemPropertyValue -Name DefaultDistribution
if ($Guid) {
GetDistroRegistryItem -All | Where-Object -Property PSChildName -eq $Guid | Select-Object -ExpandProperty DistributionName
}
}
# .SYNOPSIS
# Set the distro that should be defined as default in WSL.
# .LINK
# Get-DefaultDistro
function Set-DefaultDistro
{
[CmdletBinding(SupportsShouldProcess)]
param
(
# Name of distro. Required, cannot assume WSL default in this case!
[Parameter(Mandatory)]
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name
)
# Note: This method updates registry directly, but can also use 'wsl.exe --set-default' which updates registry through the WslService (formerly LxssManager) service.
$Item = GetDistroRegistryItem -Name $Name
if (-not $Item) {
throw "Unable to find distro with name ${Name} in registry" # Since the validation uses wsl.exe there can in theory be mismatch with what is in registry?
} elseif ($Item.PSChildName -eq (GetWSLRegistryKey | Get-ItemPropertyValue -Name DefaultDistribution)) {
Write-Warning "Specified distro is already the default"
} else {
GetWSLRegistryKey | Set-ItemProperty -Name DefaultDistribution -Value $Item.PSChildName
}
}
# .SYNOPSIS
# Get names of installed distros, from the registry (Get-Distro uses `wsl.exe`).
# .DESCRIPTION
# Returns the DistributionName from registry, which is the same distro name as
# returned by Get-Distro, although it uses wsl.exe to retrieve them instead of
# accessing registry directly.
# Note: This *should* be the same as the input, but can be used to retrieve the name
# of the implicit default, and also assuming the autocompletion/validation of Name
# parameter is result of Get-Distro it can be used to verify that information
# in registry and returned by wsl.exe is matching.
# .LINK
# Get-Distro
# Set-DistroDistributionName
function Get-DistroDistributionName
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[switch] $All
)
GetDistroRegistryItem -Name $Name -All:$All | Select-Object -ExpandProperty DistributionName
}
# .SYNOPSIS
# Change the name of an installed distro.
# .DESCRIPTION
# This simply updates the DistributionName attribute in registry.
# Note that this will set the DistributionName attribute in registry unconditionally,
# and must be used with care. Consider using Rename-Distro instead, which adds some
# safety checks and supports asking for confirmation if called with parameter -Confirm.
# Note also that this will not change the name of the directory containing the distro's
# backing files (virtual disk image) on the host system - use Set-DistroPath to do that.
# .LINK
# Rename-Distro
# Get-DistroDistributionName
# Get-Distro
# Get-DistroPath
function Set-DistroDistributionName
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()] [ValidatePattern('^[a-zA-Z0-9._-]+$')] # See https://github.com/microsoft/WSL-DistroLauncher/blob/master/DistroLauncher/DistributionInfo.h
[string] $NewName
)
GetDistroRegistryItem -Name $Name | Set-ItemProperty -Name DistributionName -Value $NewName
}
# .SYNOPSIS
# Get the name of the Universal Windows Platform (UWP) app, if the distro were installed
# through one (e.g. from Microsoft Store).
# .DESCRIPTION
# Will be empty when not installed as UWP app, such as by this script's New-Distro function.
function Get-DistroPackageName
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[switch] $All
)
GetDistroRegistryItem -Name $Name -All:$All | Select-Object -ExpandProperty PackageFamilyName
}
# .SYNOPSIS
# Get the path to a distro's backing files (virtual disk image) on the host system.
# .LINK
# Set-DistroPath
function Get-DistroPath
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[switch] $All
)
GetDistroRegistryItem -Name $Name -All:$All | Select-Object -ExpandProperty BasePath | ForEach-Object { Get-StandardizedPath $_ }
}
# .SYNOPSIS
# Change the path to the directory containing a distro's backing files (virtual
# disk image) on the host system.
# .DESCRIPTION
# This simply updates the BasePath attribute in registry.
# Note that this will not move the existing directory, and it will set the BasePath
# attribute in registry unconditionally, and therefore must be used with care.
# Consider using Move-Distro instead!
# Note also that this will not change the name of the distribution, use
# Set-DistroDistributionName to do that.
# .LINK
# Get-DistroPath
# Set-DistroDistributionName
function Set-DistroPath
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[Parameter(Mandatory)]
[string] $Path
)
# Note: This will modify the path property pointing to the directory containing the disk image
# for the distro! So this is a quite bold move! There is a method Move-Distro that will move the
# existing disk file along with.
$Item = GetDistroRegistryItem -Name $Name
if (-not $Item) { throw "Distro '${Name}' not found in registry" }
if (IsDistroItemPackageInstalled -Item $Item) {
# Not tried it, but probably not wise to move these?
throw "This distro seems to be installed as a standard UWP app, cannot change path"
}
$Item | Set-ItemProperty -Name BasePath -Value (Get-ExtendedLengthPath $Path)
}
# .SYNOPSIS
# Get the user id of the default user for a distro.
# .DESCRIPTION
# .LINK
# Set-DistroDefaultUserId
function Get-DistroDefaultUserId
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[switch] $All
)
GetDistroRegistryItem -Name $Name -All:$All | Select-Object -ExpandProperty DefaultUid
}
# .SYNOPSIS
# Set the default user of a distro
# .DESCRIPTION
# The user must be specified as the internal id integer value, as returned when
# executing `id -u` from within the distro.
# The default user is the one that other functions will run as on this distro,
# also the standard `wsl.exe` command line utility will use this user when not
# a different one is specified with arguments --user or -u.
# .LINK
# Set-DistroDefaultUserId
function Set-DistroDefaultUserId
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[Parameter(Mandatory)]
[int] $UserId # Typical values: 0 for root, 1000 for the optional default non-root user created on installation.
)
GetDistroRegistryItem -Name $Name | Set-ItemProperty -Name DefaultUid -Value $UserId
}
# .SYNOPSIS
# Get the filsystem format version used for a distro.
# .DESCRIPTION
# The possible values are 1 for "LxFs" and 2 for "WslFs".
# This is not the same as the WSL/distribution version.
function Get-DistroFileSystemVersion
{
# Get the filesystem format used. Not the same as distro / WSL version!
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]
[Alias("Distribution", "Distro")]
[string] $Name,
[switch] $All
)
$Value = GetDistroRegistryItem -Name $Name -All:$All | Select-Object -ExpandProperty Version
if ($Value -eq 1) { Write-Host "Filesystem format is LxFs"}
elseif ($Value -eq 2) { Write-Host "Filesystem format is WslFs"}
else { Write-Warning "Unknown registry value"}
$Value
}
# Options that can be stored in registry.
# Default is for distros to have all set, corresponding to int value 15.
# Documented here: https://docs.microsoft.com/en-us/windows/win32/api/wslapi/ne-wslapi-wsl_distribution_flags
# The same options, and many more, can alternatively be set in configuration
# file /etc/wsl.conf within each WSL distro (https://docs.microsoft.com/en-us/windows/wsl/wsl-config).
[Flags()]
enum DistroFlags
{
None = 0
# Whether WSL will support launching Windows processes.
# Equivalent to /etc/wsl.conf option "enabled" in section [interop].
Interop = 1
# Whether WSL will add Windows path elements to the $PATH environment variable.
# Only relevant together with Interop flag.
# Note: There are some reports that PATH traversal is slow, making command auto-completion
# in the shell also slow, and unsetting this flag fixes it.
# Equivalent to /etc/wsl.conf option "appendWindowsPath" in section [interop].
AppendWindowsPath = 2
# Whether WSL will automatically mount fixed drives (i.e C:/ or D:/) with DrvFs under /mnt.
# If not set the drives won't be mounted automatically, but can still be mounted manually or via fstab.
# Equivalent to /etc/wsl.conf option "enabled" in section [automount].
AutoMount = 4
# Undocumented value marking the distro as version 2 (WSL2).
# Must be included in enum to make it complete, to be able to parse binary int values into enum.
# There is also a separate registry value "Version" that can be used to
# find the WSL version of the distro.
Version2 = 8
}
# .SYNOPSIS
# Get the current flags value of a distro.
# .DESCRIPTION
# The flags value is a bit encoded value representing some options,
# such as whether to enable Windows-Linux Interop, automatically mount
# fixed drives from host etc.
# Default is for distros to have all options set.
# The same options, and many more, can alternatively be set in configuration
# file /etc/wsl.conf within each WSL distro.
# .LINK
# Set-DistroFlags
# Get-DistroInterop
# Get-DistroAutoMount
# Get-DistroVersion
# .LINK
# https://docs.microsoft.com/en-us/windows/win32/api/wslapi/ne-wslapi-wsl_distribution_flags
# https://docs.microsoft.com/en-us/windows/wsl/wsl-config
function Get-DistroFlags
{
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory=$false)] # Will use registered default distro if not specified
[ArgumentCompleter({CompleteDistroName @args})]
[ValidateNotNullOrEmpty()] [ValidateScript({ValidateDistroName $_})]