From 222ec0433a066c8b6479d3ee4fb6b60dce38062f Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Thu, 5 Mar 2026 16:08:32 +0100 Subject: [PATCH] Using constants rather than copied hardcoded strings --- proprietary/DiskImage/Disk/Interfaces.cs | 5 +++++ proprietary/DiskImage/Disk/Mac.cs | 16 ++++++++++------ proprietary/DiskImage/Disk/Windows.cs | 12 ++++++++---- proprietary/DiskImage/SourceProvider.cs | 15 +++++++++++++++ proprietary/DiskImage/WebModule.cs | 9 +-------- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/proprietary/DiskImage/Disk/Interfaces.cs b/proprietary/DiskImage/Disk/Interfaces.cs index 6003440ef..5140477db 100644 --- a/proprietary/DiskImage/Disk/Interfaces.cs +++ b/proprietary/DiskImage/Disk/Interfaces.cs @@ -12,6 +12,11 @@ namespace Duplicati.Proprietary.DiskImage.Disk; /// internal interface IRawDisk : IDisposable { + /// + /// Gets the platform-specific prefix for disk entries (e.g., "\\.\" on Windows, "/dev/" on Unix). + /// + static abstract string Prefix { get; } + /// /// Gets the disk identifier (e.g., "\\.\PhysicalDrive0" or "/dev/sda") /// diff --git a/proprietary/DiskImage/Disk/Mac.cs b/proprietary/DiskImage/Disk/Mac.cs index 60d486261..0f65ddf21 100644 --- a/proprietary/DiskImage/Disk/Mac.cs +++ b/proprietary/DiskImage/Disk/Mac.cs @@ -42,8 +42,12 @@ namespace Duplicati.Proprietary.DiskImage.Disk private uint m_sectorSize = 0; private long m_size = 0; private bool m_shouldFlush = false; + private const string DEVICE_PREFIX = "/dev/"; private readonly SemaphoreSlim m_ioLock = new(1, 1); + /// + public static string Prefix => "/dev/"; + /// public string DevicePath { get { return m_devicePath; } } @@ -74,9 +78,9 @@ namespace Duplicati.Proprietary.DiskImage.Disk return devicePath; // Convert /dev/diskN to /dev/rdiskN for raw/character device access - if (devicePath.StartsWith("/dev/disk") && !devicePath.StartsWith("/dev/rdisk")) + if (devicePath.StartsWith($"{DEVICE_PREFIX}disk") && !devicePath.StartsWith($"{DEVICE_PREFIX}rdisk")) { - return devicePath.Replace("/dev/disk", "/dev/rdisk"); + return devicePath.Replace($"{DEVICE_PREFIX}disk", $"{DEVICE_PREFIX}rdisk"); } return devicePath; @@ -122,7 +126,7 @@ namespace Duplicati.Proprietary.DiskImage.Disk public async Task AutoUnmountAsync(CancellationToken cancellationToken) { // Extract disk number from device path - string blockDevicePath = m_devicePath.Replace("/dev/rdisk", "/dev/disk"); + string blockDevicePath = m_devicePath.Replace($"{DEVICE_PREFIX}rdisk", $"{DEVICE_PREFIX}disk"); // Use diskutil to unmount all volumes on the disk var unmount = await ProcessRunner.RunProcessAsync("diskutil", $"unmountDisk {blockDevicePath}", 30_000, cancellationToken); @@ -495,7 +499,7 @@ namespace Duplicati.Proprietary.DiskImage.Disk if (string.IsNullOrWhiteSpace(list.Output)) yield break; - var devices = list.Output.Split("/dev/disk", StringSplitOptions.RemoveEmptyEntries); + var devices = list.Output.Split($"{DEVICE_PREFIX}disk", StringSplitOptions.RemoveEmptyEntries); foreach (var device in devices) { @@ -510,7 +514,7 @@ namespace Duplicati.Proprietary.DiskImage.Disk if (string.IsNullOrWhiteSpace(identifier)) continue; // Skip if we can't determine an identifier - var path = $"/dev/{identifier}"; + var path = $"{DEVICE_PREFIX}{identifier}"; var size = mainLine[^3..^1]; // Size is typically the third-to-last token alongside the unit (e.g., "500 GB") size[0] = size[0].Trim()[1..]; // Remove any leading/trailing whitespace, and the leading + or * var sizeinBytes = Library.Utility.Sizeparser.ParseSize(string.Join(' ', size)); @@ -529,7 +533,7 @@ namespace Duplicati.Proprietary.DiskImage.Disk var driveInfo = new PhysicalDriveInfo { - Number = identifier["/dev/disk".Length..], + Number = identifier[$"{DEVICE_PREFIX}disk".Length..], Path = path, Size = (ulong)sizeinBytes, DisplayName = displayName, diff --git a/proprietary/DiskImage/Disk/Windows.cs b/proprietary/DiskImage/Disk/Windows.cs index 94786c1b3..81839c14e 100644 --- a/proprietary/DiskImage/Disk/Windows.cs +++ b/proprietary/DiskImage/Disk/Windows.cs @@ -34,12 +34,16 @@ namespace Duplicati.Proprietary.DiskImage.Disk private uint m_sectorSize = 0; private long m_size = 0; private bool m_shouldFlush = false; + private const string DEVICE_PREFIX = @"\\.\PHYSICALDRIVE"; // Reusable aligned native buffer for zero-copy I/O private SafeHGlobalHandle? m_alignedBuffer; private int m_alignedBufferSize = 0; private readonly SemaphoreSlim m_ioLock = new(1, 1); + /// + public static string Prefix => @"\\.\"; + /// public string DevicePath { get { return m_devicePath; } } @@ -537,9 +541,9 @@ $diskInfo | ConvertTo-Json -Depth 4 foreach (var drive in drives) { var number = -1; - if (drive.Path.StartsWith("\\\\.\\PHYSICALDRIVE", StringComparison.OrdinalIgnoreCase)) + if (drive.Path.StartsWith(DEVICE_PREFIX, StringComparison.OrdinalIgnoreCase)) { - int.TryParse(drive.Path.AsSpan("\\\\.\\PHYSICALDRIVE".Length), out number); + int.TryParse(drive.Path.AsSpan(DEVICE_PREFIX.Length), out number); } drive.Number = number.ToString(); @@ -563,9 +567,9 @@ $diskInfo | ConvertTo-Json -Depth 4 { // Extract drive number from path (e.g., \\.\PHYSICALDRIVE0 -> 0) var number = -1; - if (drive.Path.StartsWith("\\\\.\\PHYSICALDRIVE", StringComparison.OrdinalIgnoreCase)) + if (drive.Path.StartsWith(DEVICE_PREFIX, StringComparison.OrdinalIgnoreCase)) { - int.TryParse(drive.Path.AsSpan("\\\\.\\PHYSICALDRIVE".Length), out number); + int.TryParse(drive.Path.AsSpan(DEVICE_PREFIX.Length), out number); } drive.Number = number.ToString(); yield return drive; diff --git a/proprietary/DiskImage/SourceProvider.cs b/proprietary/DiskImage/SourceProvider.cs index 1db3cfa4a..737df0c2d 100644 --- a/proprietary/DiskImage/SourceProvider.cs +++ b/proprietary/DiskImage/SourceProvider.cs @@ -186,6 +186,21 @@ public sealed class SourceProvider : ISourceProviderModule, IDisposable throw new PlatformNotSupportedException(Strings.PlatformNotSupported); } + /// + /// Gets the platform-specific prefix for disk entries (e.g., "\\.\" on Windows, "/dev/" on Unix). This is used to construct entry paths correctly based on the underlying platform. + /// + /// The platform-specific prefix for disk entries. + /// Thrown when the platform is not supported. + public static string GetDevicePrefix() + { + if (OperatingSystem.IsWindows()) + return Windows.Prefix; + else if (OperatingSystem.IsMacOS()) + return Mac.Prefix; + else + throw new PlatformNotSupportedException(Strings.PlatformNotSupported); + } + /// public void Dispose() { diff --git a/proprietary/DiskImage/WebModule.cs b/proprietary/DiskImage/WebModule.cs index e4e1fd86b..423d30734 100644 --- a/proprietary/DiskImage/WebModule.cs +++ b/proprietary/DiskImage/WebModule.cs @@ -58,14 +58,7 @@ public class WebModule : IWebModule return res; } - string prefix = ""; - if (OperatingSystem.IsWindows()) - prefix = "\\\\.\\"; - else if (OperatingSystem.IsMacOS()) - prefix = "/dev/"; - else - throw new PlatformNotSupportedException(Strings.PlatformNotSupported); - + var prefix = SourceProvider.GetDevicePrefix(); List parts = [.. path[prefix.Length..].Split(Path.DirectorySeparatorChar, 2)]; var physicalDrivePath = prefix + parts.First(); var subpath = parts.Last();