From 16c86d4ba951f240949929e4b033d0906affe509 Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Sun, 1 Mar 2026 11:25:44 +0100 Subject: [PATCH] Added Linux support in the disk image web module --- proprietary/DiskImage/WebModule.cs | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/proprietary/DiskImage/WebModule.cs b/proprietary/DiskImage/WebModule.cs index ac0417972..a84d7e8ea 100644 --- a/proprietary/DiskImage/WebModule.cs +++ b/proprietary/DiskImage/WebModule.cs @@ -95,6 +95,8 @@ public class WebModule : IWebModule return await WindowsListPhysicalDrives(cancellationToken); else if (OperatingSystem.IsMacOS()) return await MacListPhysicalDrives(cancellationToken); + else if (OperatingSystem.IsLinux()) + return await LinuxListPhysicalDrives(cancellationToken); else throw new PlatformNotSupportedException(Strings.PlatformNotSupported); @@ -103,6 +105,8 @@ public class WebModule : IWebModule prefix = "\\\\.\\"; else if (OperatingSystem.IsMacOS()) prefix = "/dev/"; + else if (OperatingSystem.IsLinux()) + prefix = "/dev/"; else throw new PlatformNotSupportedException(Strings.PlatformNotSupported); @@ -272,6 +276,124 @@ $diskInfo | ConvertTo-Json -Depth 4 return result; } + private async Task> LinuxListPhysicalDrives(CancellationToken cancellationToken) + { + if (!OperatingSystem.IsLinux()) + throw new PlatformNotSupportedException(Strings.PlatformNotSupported); + + var result = new Dictionary(); + + try + { + // Enumerate block devices from /sys/block/ + // This is a virtual filesystem present on all Linux distributions + const string sysBlockPath = "/sys/block/"; + if (!Directory.Exists(sysBlockPath)) + return result; + + var devices = Directory.GetDirectories(sysBlockPath) + .Select(d => Path.GetFileName(d)) + .Where(device => !string.IsNullOrEmpty(device)) + .Where(device => !device.StartsWith("loop", StringComparison.OrdinalIgnoreCase)) + .Where(device => !device.StartsWith("ram", StringComparison.OrdinalIgnoreCase)) + .Where(device => !device.StartsWith("dm-", StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + foreach (var device in devices) + { + try + { + var devicePath = $"/dev/{device}"; + + // Read size in 512-byte sectors from /sys/block/{dev}/size + var sizePath = Path.Combine(sysBlockPath, device, "size"); + long sizeInSectors = 0; + if (File.Exists(sizePath)) + { + var sizeText = await File.ReadAllTextAsync(sizePath, cancellationToken); + long.TryParse(sizeText.Trim(), out sizeInSectors); + } + var sizeInBytes = (ulong)(sizeInSectors * 512); + + // Try to get display name from /sys/block/{dev}/device/model + var modelPath = Path.Combine(sysBlockPath, device, "device", "model"); + string displayName = device; + if (File.Exists(modelPath)) + { + var modelText = await File.ReadAllTextAsync(modelPath, cancellationToken); + displayName = modelText.Trim(); + } + + // Get mount points by reading /proc/mounts + var mountPoints = GetLinuxMountPoints(device); + + var driveInfo = new PhysicalDriveInfo + { + Number = device, + Path = devicePath, + Size = sizeInBytes, + DisplayName = displayName, + Guid = null, // Linux block devices don't have a single GUID like Windows or macOS + MountPoints = mountPoints, + Online = true // Physical drives are always considered online + }; + + AddDiskToResult(driveInfo, result); + } + catch (Exception ex) + { + Log.WriteWarningMessage(LOGTAG, "LinuxDriveEnumerationFailed", ex, $"Failed to enumerate drive: {device}"); + } + } + } + catch (Exception ex) + { + Log.WriteErrorMessage(LOGTAG, "LinuxDriveListFailed", ex, "Failed to list physical drives on Linux"); + } + + return result; + } + + private string[] GetLinuxMountPoints(string device) + { + var mountPoints = new List(); + + try + { + // Read /proc/mounts to find mount points for this device + if (File.Exists("/proc/mounts")) + { + var mountsText = File.ReadAllText("/proc/mounts"); + var lines = mountsText.Split(['\n'], StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + var parts = line.Split([' '], StringSplitOptions.RemoveEmptyEntries); + if (parts.Length >= 2) + { + var source = parts[0]; + var mountPoint = parts[1]; + + // Check if this mount is for our device or one of its partitions + // Device: /dev/sda, Partitions: /dev/sda1, /dev/sda2, etc. + if (source == $"/dev/{device}" || source.StartsWith($"/dev/{device}", StringComparison.OrdinalIgnoreCase)) + { + // Decode escaped spaces in mount point + mountPoint = mountPoint.Replace("\\040", " "); + mountPoints.Add(mountPoint); + } + } + } + } + } + catch (Exception ex) + { + Log.WriteWarningMessage(LOGTAG, "LinuxMountPointReadFailed", ex, $"Failed to read mount points for {device}"); + } + + return mountPoints.ToArray(); + } + private void AddDiskToResult(PhysicalDriveInfo drive, Dictionary result) { // For now, we only allow picking full disks, not individual partitions