|
7 | 7 |
|
8 | 8 | namespace GitCredentialManager
|
9 | 9 | {
|
| 10 | + public enum WindowsShell |
| 11 | + { |
| 12 | + Cmd, |
| 13 | + PowerShell, |
| 14 | + } |
| 15 | + |
10 | 16 | public static class WslUtils
|
11 | 17 | {
|
12 | 18 | private const string WslUncPrefix = @"\\wsl$\";
|
13 | 19 | private const string WslLocalHostUncPrefix = @"\\wsl.localhost\";
|
14 | 20 | private const string WslCommandName = "wsl.exe";
|
15 | 21 | private const string WslInteropEnvar = "WSL_INTEROP";
|
| 22 | + private const string WslConfFilePath = "/etc/wsl.conf"; |
| 23 | + private const string DefaultWslMountPrefix = "/mnt"; |
| 24 | + private const string DefaultWslSysDriveMountName = "c"; |
16 | 25 |
|
17 | 26 | /// <summary>
|
18 | 27 | /// Cached WSL version.
|
19 | 28 | /// </summary>
|
20 | 29 | /// <remarks>A value of 0 represents "not WSL", and a value less than 0 represents "unknown".</remarks>
|
21 | 30 | private static int _wslVersion = -1;
|
22 | 31 |
|
| 32 | + /// <summary> |
| 33 | + /// Cached Windows system drive mount path. |
| 34 | + /// </summary> |
| 35 | + private static string _sysDriveMountPath = null; |
| 36 | + |
23 | 37 | public static bool IsWslDistribution(IEnvironment env, IFileSystem fs, out int wslVersion)
|
24 | 38 | {
|
25 | 39 | if (_wslVersion < 0)
|
@@ -113,6 +127,93 @@ public static ChildProcess CreateWslProcess(string distribution,
|
113 | 127 | return new ChildProcess(trace2, psi);
|
114 | 128 | }
|
115 | 129 |
|
| 130 | + /// <summary> |
| 131 | + /// Create a command to be executed in a shell in the host Windows operating system. |
| 132 | + /// </summary> |
| 133 | + /// <param name="fs">File system.</param> |
| 134 | + /// <param name="shell">Shell used to execute the command in Windows.</param> |
| 135 | + /// <param name="command">Command to execute.</param> |
| 136 | + /// <param name="workingDirectory">Optional working directory.</param> |
| 137 | + /// <returns><see cref="Process"/> object ready to start.</returns> |
| 138 | + public static Process CreateWindowsShellProcess(IFileSystem fs, |
| 139 | + WindowsShell shell, string command, string workingDirectory = null) |
| 140 | + { |
| 141 | + string sysDrive = GetSystemDriveMountPath(fs); |
| 142 | + |
| 143 | + string launcher; |
| 144 | + var args = new StringBuilder(); |
| 145 | + |
| 146 | + switch (shell) |
| 147 | + { |
| 148 | + case WindowsShell.Cmd: |
| 149 | + launcher = Path.Combine(sysDrive, "Windows/cmd.exe"); |
| 150 | + args.AppendFormat("/C {0}", command); |
| 151 | + break; |
| 152 | + |
| 153 | + case WindowsShell.PowerShell: |
| 154 | + const string psStreamSetup = |
| 155 | + "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; " + |
| 156 | + "[Console]::InputEncoding = [System.Text.Encoding]::UTF8; "; |
| 157 | + |
| 158 | + launcher = Path.Combine(sysDrive, "Windows/System32/WindowsPowerShell/v1.0/powershell.exe"); |
| 159 | + args.Append(" -NoProfile -NonInteractive -ExecutionPolicy Bypass"); |
| 160 | + args.AppendFormat(" -Command \"{0} {1}\"", psStreamSetup, command); |
| 161 | + break; |
| 162 | + |
| 163 | + default: |
| 164 | + throw new ArgumentOutOfRangeException(nameof(shell)); |
| 165 | + } |
| 166 | + |
| 167 | + var psi = new ProcessStartInfo(launcher, args.ToString()) |
| 168 | + { |
| 169 | + RedirectStandardInput = true, |
| 170 | + RedirectStandardOutput = true, |
| 171 | + RedirectStandardError = true, |
| 172 | + UseShellExecute = false, |
| 173 | + WorkingDirectory = workingDirectory ?? string.Empty |
| 174 | + }; |
| 175 | + |
| 176 | + return new Process { StartInfo = psi }; |
| 177 | + } |
| 178 | + |
| 179 | + private static string GetSystemDriveMountPath(IFileSystem fs) |
| 180 | + { |
| 181 | + if (_sysDriveMountPath is null) |
| 182 | + { |
| 183 | + string mountPrefix = DefaultWslMountPrefix; |
| 184 | + |
| 185 | + // If the wsl.conf file exists in this distribution the user may |
| 186 | + // have changed the Windows volume mount point prefix. Use it! |
| 187 | + if (fs.FileExists(WslConfFilePath)) |
| 188 | + { |
| 189 | + // Read wsl.conf for [automount] root = <path> |
| 190 | + IniFile wslConf = IniSerializer.Deserialize(fs, WslConfFilePath); |
| 191 | + if (wslConf.TryGetSection("automount", out IniSection automountSection) && |
| 192 | + automountSection.TryGetProperty("root", out string value)) |
| 193 | + { |
| 194 | + mountPrefix = value; |
| 195 | + } |
| 196 | + } |
| 197 | + |
| 198 | + // Try to locate the system volume by looking for the Windows\System32 directory |
| 199 | + IEnumerable<string> mountPoints = fs.EnumerateDirectories(mountPrefix); |
| 200 | + foreach (string mountPoint in mountPoints) |
| 201 | + { |
| 202 | + string sys32Path = Path.Combine(mountPoint, "Windows", "System32"); |
| 203 | + |
| 204 | + if (fs.DirectoryExists(sys32Path)) |
| 205 | + { |
| 206 | + _sysDriveMountPath = mountPoint; |
| 207 | + return _sysDriveMountPath; |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + _sysDriveMountPath = Path.Combine(mountPrefix, DefaultWslSysDriveMountName); |
| 212 | + } |
| 213 | + |
| 214 | + return _sysDriveMountPath; |
| 215 | + } |
| 216 | + |
116 | 217 | public static string ConvertToDistroPath(string path, out string distribution)
|
117 | 218 | {
|
118 | 219 | if (!IsWslPath(path)) throw new ArgumentException("Must provide a WSL path", nameof(path));
|
|
0 commit comments