Have auto detection of file system on partition when the type cannot be determined from the partition table
This commit is contained in:
@@ -270,6 +270,37 @@ internal class Fat32Filesystem : IFilesystem
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to detect whether a given partition has a FAT32 file system.
|
||||
/// </summary>
|
||||
/// <param name="disk">The raw disk to check for the filesystem.</param>
|
||||
/// <param name="offset">The offset on the disk where the partition starts.</param>
|
||||
/// <param name="size">The size of the partition.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that resolves to true if a valid FAT32 filesystem is detected, otherwise false.</returns>
|
||||
public static async Task<bool> DetectAsync(IRawDisk disk, long offset, int size, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bootSectorData = new byte[512];
|
||||
using var stream = await disk.ReadBytesAsync(offset, size, cancellationToken);
|
||||
var bytesRead = await stream.ReadAsync(bootSectorData, cancellationToken);
|
||||
if (bytesRead < 512)
|
||||
return false;
|
||||
|
||||
// Try FAT32: the constructor validates the boot sector signature and
|
||||
// the "FAT32" filesystem-type string at offset 0x52.
|
||||
_ = new Fat32BootSector(bootSectorData);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Unable to read the partition
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.Library.Utility;
|
||||
using Duplicati.Proprietary.DiskImage.Disk;
|
||||
using Duplicati.Proprietary.DiskImage.Filesystem;
|
||||
using Duplicati.Proprietary.DiskImage.General;
|
||||
|
||||
namespace Duplicati.Proprietary.DiskImage.Partition;
|
||||
@@ -391,7 +393,7 @@ internal class GPT : IPartitionTable
|
||||
PartitionType partitionType = DeterminePartitionType(typeGuid);
|
||||
|
||||
// Determine filesystem type based on partition name and known patterns
|
||||
FileSystemType fsType = DetermineFilesystemType(name, typeGuid);
|
||||
FileSystemType fsType = DetermineFilesystemType(startOffset, (int)size, typeGuid.ToString(), CancellationToken.None);
|
||||
|
||||
return new BasePartition
|
||||
{
|
||||
@@ -421,40 +423,27 @@ internal class GPT : IPartitionTable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the filesystem type based on the partition name and type GUID.
|
||||
/// Uses heuristics based on common naming patterns and known GUIDs.
|
||||
/// Determines the filesystem type based on the partition type GUID and partition data.
|
||||
/// </summary>
|
||||
/// <param name="name">The partition name.</param>
|
||||
/// <param name="typeGuid">The partition type GUID.</param>
|
||||
/// <returns>The corresponding <see cref="FileSystemType"/>.</returns>
|
||||
private static FileSystemType DetermineFilesystemType(string name, Guid typeGuid)
|
||||
private FileSystemType DetermineFilesystemType(long offset, int size, string typeGuid, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
// First, we try to look at the GUID
|
||||
if (typeGuid == "48465300-0000-11AA-AA11-00306543ECAC")
|
||||
return FileSystemType.HFSPlus;
|
||||
|
||||
if (typeGuid == "7C3457EF-0000-11AA-AA11-00306543ECAC")
|
||||
return FileSystemType.APFS;
|
||||
|
||||
if (m_rawDisk is not null)
|
||||
{
|
||||
var upperName = name.ToUpperInvariant();
|
||||
if (upperName.Contains("NTFS")) return FileSystemType.NTFS;
|
||||
if (upperName.Contains("FAT32")) return FileSystemType.FAT32;
|
||||
if (upperName.Contains("FAT16")) return FileSystemType.FAT16;
|
||||
if (upperName.Contains("FAT12")) return FileSystemType.FAT12;
|
||||
if (upperName.Contains("EXFAT")) return FileSystemType.ExFAT;
|
||||
if (upperName.Contains("HFS")) return FileSystemType.HFSPlus;
|
||||
if (upperName.Contains("APFS")) return FileSystemType.APFS;
|
||||
if (upperName.Contains("EXT4")) return FileSystemType.Ext4;
|
||||
if (upperName.Contains("EXT3")) return FileSystemType.Ext3;
|
||||
if (upperName.Contains("EXT2")) return FileSystemType.Ext2;
|
||||
if (upperName.Contains("XFS")) return FileSystemType.XFS;
|
||||
if (upperName.Contains("BTRFS")) return FileSystemType.Btrfs;
|
||||
if (upperName.Contains("ZFS")) return FileSystemType.ZFS;
|
||||
if (upperName.Contains("REFS")) return FileSystemType.ReFS;
|
||||
if (Fat32Filesystem.DetectAsync(m_rawDisk, offset, size, cancellationToken).Await())
|
||||
return FileSystemType.FAT32;
|
||||
}
|
||||
|
||||
// Fallback based on GUID
|
||||
return typeGuid.ToString().ToUpper() switch
|
||||
{
|
||||
"48465300-0000-11AA-AA11-00306543ECAC" => FileSystemType.HFSPlus,
|
||||
"7C3457EF-0000-11AA-AA11-00306543ECAC" => FileSystemType.APFS,
|
||||
_ => FileSystemType.Unknown
|
||||
};
|
||||
return FileSystemType.Unknown;
|
||||
}
|
||||
|
||||
// GPT-specific properties
|
||||
|
||||
@@ -4,7 +4,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Duplicati.Library.Utility;
|
||||
using Duplicati.Proprietary.DiskImage.Disk;
|
||||
using Duplicati.Proprietary.DiskImage.Filesystem;
|
||||
using Duplicati.Proprietary.DiskImage.General;
|
||||
|
||||
namespace Duplicati.Proprietary.DiskImage.Partition;
|
||||
@@ -135,7 +137,7 @@ internal class MBR : IPartitionTable
|
||||
StartOffset = entry.StartLBA * m_bytesPerSector,
|
||||
Size = entry.SizeInSectors * m_bytesPerSector,
|
||||
Name = $"Partition {i + 1}",
|
||||
FilesystemType = DetermineFilesystemType(entry),
|
||||
FilesystemType = entry.FilesystemType,
|
||||
VolumeGuid = null,
|
||||
RawDisk = m_rawDisk,
|
||||
StartingLba = entry.StartLBA,
|
||||
@@ -185,6 +187,7 @@ internal class MBR : IPartitionTable
|
||||
{
|
||||
EntryNumber = entryNumber,
|
||||
PartitionType = DeterminePartitionType(partitionType),
|
||||
FilesystemType = DetermineFilesystemType(partitionType, startLBA * m_bytesPerSector, (int)(sizeInSectors * m_bytesPerSector)),
|
||||
PartitionTypeByte = partitionType,
|
||||
StartLBA = startLBA,
|
||||
SizeInSectors = sizeInSectors,
|
||||
@@ -210,11 +213,21 @@ internal class MBR : IPartitionTable
|
||||
/// <summary>
|
||||
/// Determines the filesystem type based on the partition type byte.
|
||||
/// </summary>
|
||||
/// <param name="entry">The MBR partition entry.</param>
|
||||
/// <param name="typeByte">The partition type byte from the MBR.</param>
|
||||
/// <param name="offset">The offset of the partition within the disk.</param>
|
||||
/// <param name="size">The size of the partition in bytes.</param>
|
||||
/// <returns>The determined filesystem type.</returns>
|
||||
private static FileSystemType DetermineFilesystemType(MBRPartitionEntry entry)
|
||||
private FileSystemType DetermineFilesystemType(byte typeByte, long offset, int size)
|
||||
{
|
||||
return MbrPartitionTypes.ToFilesystemType(entry.PartitionTypeByte);
|
||||
var typeByByte = MbrPartitionTypes.ToFilesystemType(typeByte);
|
||||
if (typeByByte is FileSystemType.Unknown && m_rawDisk != null)
|
||||
{
|
||||
// If the partition type byte is unknown, attempt to detect the filesystem by reading the partition data
|
||||
if (Fat32Filesystem.DetectAsync(m_rawDisk, offset, size, CancellationToken.None).Await())
|
||||
return FileSystemType.FAT32;
|
||||
}
|
||||
|
||||
return typeByByte;
|
||||
}
|
||||
|
||||
// MBR-specific properties
|
||||
@@ -318,6 +331,9 @@ internal class MBR : IPartitionTable
|
||||
/// <summary>Gets the partition type.</summary>
|
||||
public PartitionType PartitionType { get; init; }
|
||||
|
||||
/// <summary>Gets the filesystem type.</summary>
|
||||
public FileSystemType FilesystemType { get; init; }
|
||||
|
||||
/// <summary>Gets the raw partition type byte.</summary>
|
||||
public byte PartitionTypeByte { get; init; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user