-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Expand file tree
/
Copy pathVirtualDriveHelper.Windows.cs
More file actions
148 lines (131 loc) · 5.35 KB
/
Copy pathVirtualDriveHelper.Windows.cs
File metadata and controls
148 lines (131 loc) · 5.35 KB
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Versioning;
namespace System.IO
{
// Adds test helper APIs to manipulate Windows virtual drives via SUBST.
[SupportedOSPlatform("windows")]
public class VirtualDriveHelper : IDisposable
{
// Temporary Windows directory that can be mounted to a drive letter using the subst command
private string? _virtualDriveTargetDir = null;
// Windows drive letter that points to a mounted directory using the subst command
private char _virtualDriveLetter = default;
/// <summary>
/// If there is a SUBST'ed drive, Dispose unmounts it to free the drive letter.
/// </summary>
public void Dispose()
{
try
{
if (VirtualDriveLetter != default)
{
DeleteVirtualDrive(VirtualDriveLetter);
Directory.Delete(VirtualDriveTargetDir, recursive: true);
}
}
catch { } // avoid exceptions on dispose
}
/// <summary>
/// Returns the path of a folder that is to be mounted using SUBST.
/// </summary>
public string VirtualDriveTargetDir
{
get
{
if (_virtualDriveTargetDir == null)
{
// Create a folder inside the temp directory so that it can be mounted to a drive letter with subst
_virtualDriveTargetDir = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_virtualDriveTargetDir);
}
return _virtualDriveTargetDir;
}
}
/// <summary>
/// Returns the drive letter of a drive letter that represents a mounted folder using SUBST.
/// </summary>
public char VirtualDriveLetter
{
get
{
if (_virtualDriveLetter == default)
{
// Mount the folder to a drive letter
_virtualDriveLetter = CreateVirtualDrive(VirtualDriveTargetDir);
}
return _virtualDriveLetter;
}
}
///<summary>
/// On Windows, mounts a folder to an assigned virtual drive letter using the subst command.
/// subst is not available in Windows Nano.
/// </summary>
private static char CreateVirtualDrive(string targetDir)
{
char driveLetter = GetNextAvailableDriveLetter();
bool success = RunProcess(CreateProcessStartInfo("cmd", "/c", SubstPath, $"{driveLetter}:", targetDir));
if (!success || !DriveInfo.GetDrives().Any(x => x.Name[0] == driveLetter))
{
throw new InvalidOperationException($"Could not create virtual drive {driveLetter}: with subst");
}
return driveLetter;
// Finds the next unused drive letter and returns it.
char GetNextAvailableDriveLetter()
{
List<char> existingDrives = DriveInfo.GetDrives().Select(x => x.Name[0]).ToList();
// A,B are reserved, C is usually reserved
IEnumerable<int> range = Enumerable.Range('D', 'Z' - 'D');
IEnumerable<char> castRange = range.Select(x => Convert.ToChar(x));
IEnumerable<char> allDrivesLetters = castRange.Except(existingDrives);
if (!allDrivesLetters.Any())
{
throw new ArgumentOutOfRangeException("No drive letters available");
}
return allDrivesLetters.First();
}
}
/// <summary>
/// On Windows, unassigns the specified virtual drive letter from its mounted folder.
/// </summary>
private static void DeleteVirtualDrive(char driveLetter)
{
bool success = RunProcess(CreateProcessStartInfo("cmd", "/c", SubstPath, "/d", $"{driveLetter}:"));
if (!success || DriveInfo.GetDrives().Any(x => x.Name[0] == driveLetter))
{
throw new InvalidOperationException($"Could not delete virtual drive {driveLetter}: with subst");
}
}
private static ProcessStartInfo CreateProcessStartInfo(string fileName, params string[] arguments)
{
var info = new ProcessStartInfo
{
FileName = fileName,
UseShellExecute = false,
RedirectStandardOutput = true
};
foreach (var argument in arguments)
{
info.ArgumentList.Add(argument);
}
return info;
}
private static bool RunProcess(ProcessStartInfo startInfo)
{
using var process = Process.Start(startInfo);
process.WaitForExit();
return process.ExitCode == 0;
}
private static string SubstPath
{
get
{
string systemRoot = Environment.GetEnvironmentVariable("SystemRoot") ?? @"C:\Windows";
return Path.Join(systemRoot, "System32", "subst.exe");
}
}
}
}