Skip to content

Commit

Permalink
Fix sector size calculation for Disk images
Browse files Browse the repository at this point in the history
Fix for: DiscUtils#247
  • Loading branch information
LTRData committed Feb 24, 2024
1 parent 8ddb30a commit ca3b9c3
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 5 deletions.
18 changes: 14 additions & 4 deletions Library/DiscUtils.Iso9660/CDBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,22 +261,32 @@ protected override List<BuilderExtent> FixExtents(out long totalLength)
long bootCatalogPos = 0;
if (_bootEntry != null)
{
// Boot catalog MUST be at beginning
bootCatalogPos = focus;
focus += IsoUtilities.SectorSize;
var bootImagePos = focus;
var realBootImage = PatchBootImage(_bootImage, (uint)(DiskStart / IsoUtilities.SectorSize),
(uint)(bootImagePos / IsoUtilities.SectorSize));
var bootImageExtent = new BuilderStreamExtent(focus, realBootImage);
fixedRegions.Add(bootImageExtent);
focus += MathUtilities.RoundUp(bootImageExtent.Length, IsoUtilities.SectorSize);

bootCatalogPos = focus;
var bootCatalog = new byte[IsoUtilities.SectorSize];
var bve = new BootValidationEntry();
bve.WriteTo(bootCatalog, 0x00);
_bootEntry.ImageStart = (uint)MathUtilities.Ceil(bootImagePos, IsoUtilities.SectorSize);
_bootEntry.SectorCount = (ushort)MathUtilities.Ceil(_bootImage.Length, Sizes.Sector);
if (_bootEntry.BootMediaType != BootDeviceEmulation.NoEmulation)
{
_bootEntry.SectorCount = 1;
}
else
{
_bootEntry.SectorCount = (ushort)MathUtilities.Ceil(_bootImage.Length, Sizes.Sector);
}
_bootEntry.WriteTo(bootCatalog, 0x20);
fixedRegions.Add(new BuilderBufferExtent(bootCatalogPos, bootCatalog));
focus += IsoUtilities.SectorSize;

// Don't add to focus, we already skipped the length of the bootCatalog
}

// ####################################################################
Expand Down Expand Up @@ -552,7 +562,7 @@ private BuildDirectoryInfo TryGetDirectory(ReadOnlyMemory<char>[] path, int path
}
else
{
if (!(next is BuildDirectoryInfo nextAsBuildDirectoryInfo))
if (next is not BuildDirectoryInfo nextAsBuildDirectoryInfo)
{
throw new IOException("File with conflicting name exists");
}
Expand Down
84 changes: 83 additions & 1 deletion Library/DiscUtils.Iso9660/VfsCDReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,95 @@ public UnixFileSystemInfo GetUnixFileInfo(string path)
return file.UnixFileInfo;
}

/// <summary>
/// Detects size of FAT file system from a disk's boot sector.
/// </summary>
/// <param name="stream">The stream to inspect.</param>
/// <returns><c>Sector count</c> if the stream appears to be a FAT file system, else <c>0</c>.</returns>
public static ushort DetectSectorCountFromBootSector(Stream stream)
{
if (stream.Length < 512)
{
return 0;
}

stream.Position = 0;
var bytes = stream.ReadExactly(512);
var bpbBytesPerSec = EndianUtilities.ToUInt16LittleEndian(bytes, 11);
if (bpbBytesPerSec != 512)
{
return 0;
}

var bpbNumFATs = bytes[16];
if (bpbNumFATs is 0 or > 2)
{
return 0;
}

var bpbTotSec16 = EndianUtilities.ToUInt16LittleEndian(bytes, 19);
var bpbTotSec32 = EndianUtilities.ToUInt32LittleEndian(bytes, 32);

if (!((bpbTotSec16 == 0) ^ (bpbTotSec32 == 0)))
{
return 0;
}

var totalSectors = bpbTotSec16 + bpbTotSec32;
// Can't be greater than unsigned short
if (totalSectors > 65535)
{
return 0;
}
return (ushort)totalSectors;
}


/// <summary>
/// Returns the actual sector count for a boot image (vs. reported).
///
/// Boot Images with MBR or floppy boot sectors will have boot Entries that
/// list their sector count as 1.
/// This is incorrect and is either determined by the emulation type or what
/// the boot sector lists the partition as having.
/// </summary>
/// <returns>int: sector count in 512 bytes</returns>
public long GetActualBootImageSectorCount()
{
switch (BootEmulation)
{
case BootDeviceEmulation.Diskette1440KiB:
return 1440 * 1024 / Sizes.Sector;
case BootDeviceEmulation.Diskette1200KiB:
return 1200 * 1024 / Sizes.Sector;
case BootDeviceEmulation.Diskette2880KiB:
return 2880 * 1024 / Sizes.Sector;
case BootDeviceEmulation.HardDisk:
case BootDeviceEmulation.NoEmulation:
default:
var initialEntry = GetBootInitialEntry();
var BytesToStart = initialEntry.ImageStart * IsoUtilities.SectorSize;
var sectorCount = DetectSectorCountFromBootSector(
new SubStream(_data, BytesToStart, Sizes.Sector)
);
// Invalid length read from BootSector
if (sectorCount == 0 || sectorCount * Sizes.Sector + BytesToStart > _data.Length)
{
sectorCount = initialEntry.SectorCount;
}
return sectorCount;
}

}

public Stream OpenBootImage()
{
var initialEntry = GetBootInitialEntry();
var sectorCount = GetActualBootImageSectorCount();
if (initialEntry != null)
{
return new SubStream(_data, initialEntry.ImageStart * IsoUtilities.SectorSize,
initialEntry.SectorCount * Sizes.Sector);
sectorCount * Sizes.Sector);
}
throw new InvalidOperationException("No valid boot image");
}
Expand Down

0 comments on commit ca3b9c3

Please sign in to comment.