|
| 1 | +using System.Management; |
1 | 2 | using System.Security.Cryptography; |
| 3 | +using DeepPurge.Core.Diagnostics; |
2 | 4 |
|
3 | 5 | namespace DeepPurge.Core.Safety; |
4 | 6 |
|
@@ -104,4 +106,79 @@ public static bool WipeDirectory(string path) |
104 | 106 | return false; |
105 | 107 | } |
106 | 108 | } |
| 109 | + |
| 110 | + public static async Task<long> WipeFreeSpaceAsync( |
| 111 | + string drivePath, |
| 112 | + IProgress<string>? progress = null, |
| 113 | + CancellationToken ct = default) |
| 114 | + { |
| 115 | + var driveRoot = Path.GetPathRoot(drivePath) ?? @"C:\"; |
| 116 | + var isSsd = DetectSsd(driveRoot); |
| 117 | + progress?.Report(isSsd ? $"SSD detected on {driveRoot} — single-pass fill" : $"HDD detected on {driveRoot} — single-pass fill"); |
| 118 | + |
| 119 | + var tempDir = Path.Combine(driveRoot, ".deeppurge_wipe_" + Guid.NewGuid().ToString("N")[..8]); |
| 120 | + long totalWritten = 0; |
| 121 | + try |
| 122 | + { |
| 123 | + Directory.CreateDirectory(tempDir); |
| 124 | + var fillBuffer = new byte[1024 * 1024]; |
| 125 | + int fileIndex = 0; |
| 126 | + |
| 127 | + while (true) |
| 128 | + { |
| 129 | + ct.ThrowIfCancellationRequested(); |
| 130 | + var filePath = Path.Combine(tempDir, $"wipe_{fileIndex++}.tmp"); |
| 131 | + try |
| 132 | + { |
| 133 | + using var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, fillBuffer.Length, FileOptions.WriteThrough); |
| 134 | + while (true) |
| 135 | + { |
| 136 | + ct.ThrowIfCancellationRequested(); |
| 137 | + RandomNumberGenerator.Fill(fillBuffer); |
| 138 | + fs.Write(fillBuffer, 0, fillBuffer.Length); |
| 139 | + totalWritten += fillBuffer.Length; |
| 140 | + if (totalWritten % (100L * 1024 * 1024) == 0) |
| 141 | + progress?.Report($"Filling free space: {totalWritten / (1024 * 1024)} MB written..."); |
| 142 | + } |
| 143 | + } |
| 144 | + catch (IOException) |
| 145 | + { |
| 146 | + break; |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | + catch (OperationCanceledException) { throw; } |
| 151 | + catch (Exception ex) { Log.Warn($"WipeFreeSpace: {ex.Message}"); } |
| 152 | + finally |
| 153 | + { |
| 154 | + try |
| 155 | + { |
| 156 | + if (Directory.Exists(tempDir)) |
| 157 | + Directory.Delete(tempDir, recursive: true); |
| 158 | + } |
| 159 | + catch { } |
| 160 | + } |
| 161 | + |
| 162 | + progress?.Report($"Free space wipe complete: {totalWritten / (1024 * 1024)} MB overwritten"); |
| 163 | + return totalWritten; |
| 164 | + } |
| 165 | + |
| 166 | + private static bool DetectSsd(string driveRoot) |
| 167 | + { |
| 168 | + try |
| 169 | + { |
| 170 | + var driveLetter = driveRoot.TrimEnd('\\').TrimEnd(':'); |
| 171 | + using var searcher = new ManagementObjectSearcher( |
| 172 | + $"SELECT MediaType FROM MSFT_PhysicalDisk WHERE DeviceID IN " + |
| 173 | + $"(SELECT DiskNumber FROM MSFT_Partition WHERE DriveLetter='{driveLetter}')"); |
| 174 | + searcher.Scope = new ManagementScope(@"\\.\ROOT\Microsoft\Windows\Storage"); |
| 175 | + foreach (var obj in searcher.Get()) |
| 176 | + { |
| 177 | + var mediaType = Convert.ToInt32(obj["MediaType"]); |
| 178 | + return mediaType == 4; // 4 = SSD, 3 = HDD |
| 179 | + } |
| 180 | + } |
| 181 | + catch { } |
| 182 | + return false; |
| 183 | + } |
107 | 184 | } |
0 commit comments