public static async Task <IEnumerable <LoadSegBlock> > Read( RigidDiskBlock rigidDiskBlock, FileSystemHeaderBlock fileSystemHeaderBlock, Stream stream) { var loadSegBlocks = new List <LoadSegBlock>(); var segListBlock = fileSystemHeaderBlock.SegListBlocks; do { // calculate seg list block offset var segListBlockOffset = rigidDiskBlock.BlockSize * segListBlock; // seek partition block offset stream.Seek(segListBlockOffset, SeekOrigin.Begin); // read block var block = await BlockHelper.ReadBlock(stream); // parse file system header block var loadSegBlock = await Parse(block); loadSegBlocks.Add(loadSegBlock); // get next partition list block and increase partition number segListBlock = loadSegBlock.NextLoadSegBlock; } while (segListBlock > 0); return(loadSegBlocks); }
public static void ResetBadBlockPointers(RigidDiskBlock rigidDiskBlock) { rigidDiskBlock.BadBlockList = 0; foreach (var badBlock in rigidDiskBlock.BadBlocks) { ResetBadBlockPointers(badBlock); } }
public static void ResetFileSystemHeaderBlockPointers(RigidDiskBlock rigidDiskBlock) { rigidDiskBlock.FileSysHdrList = 0; foreach (var fileSystemHeaderBlock in rigidDiskBlock.FileSystemHeaderBlocks) { ResetFileSystemHeaderBlockPointers(fileSystemHeaderBlock); } }
public static void ResetPartitionBlockPointers(RigidDiskBlock rigidDiskBlock) { rigidDiskBlock.PartitionList = 0; foreach (var partitionBlock in rigidDiskBlock.PartitionBlocks) { ResetPartitionBlockPointers(partitionBlock); } }
public static async Task <IEnumerable <PartitionBlock> > Read(RigidDiskBlock rigidDiskBlock, Stream stream) { if (rigidDiskBlock.PartitionList == BlockIdentifiers.EndOfBlock) { return(Enumerable.Empty <PartitionBlock>()); } // get partition list block and set partition number to 1 var partitionList = rigidDiskBlock.PartitionList; var partitionBlocks = new List <PartitionBlock>(); do { // calculate partition block offset var partitionBlockOffset = rigidDiskBlock.BlockSize * partitionList; // seek partition block offset stream.Seek(partitionBlockOffset, SeekOrigin.Begin); // read block var blockBytes = await BlockHelper.ReadBlock(stream); // read partition block var partitionBlock = await Parse(rigidDiskBlock, blockBytes); // fail, if partition block is null if (partitionBlock == null) { throw new IOException("Invalid partition block"); } partitionBlocks.Add(partitionBlock); // get next partition list block and increase partition number partitionList = partitionBlock.NextPartitionBlock; } while (partitionList > 0 && partitionList != BlockIdentifiers.EndOfBlock); rigidDiskBlock.PartitionBlocks = partitionBlocks; rigidDiskBlock.FileSystemHeaderBlocks = await FileSystemHeaderBlockReader.Read(rigidDiskBlock, stream); return(partitionBlocks); }
public static async Task <IEnumerable <BadBlock> > Read(RigidDiskBlock rigidDiskBlock, Stream stream) { if (rigidDiskBlock == null) { throw new ArgumentNullException(nameof(rigidDiskBlock)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (rigidDiskBlock.BadBlockList == BlockIdentifiers.EndOfBlock) { return(Enumerable.Empty <BadBlock>()); } var badBlocks = new List <BadBlock>(); var badBlockList = rigidDiskBlock.BadBlockList; do { // calculate block offset var blockOffset = rigidDiskBlock.BlockSize * badBlockList; // seek block offset stream.Seek(blockOffset, SeekOrigin.Begin); // read block var block = await BlockHelper.ReadBlock(stream); // read rigid disk block var badBlock = await Parse(block); badBlocks.Add(badBlock); // get next partition list block and increase partition number badBlockList = badBlock.NextBadBlock; } while (badBlockList > 0 && badBlockList != BlockIdentifiers.EndOfBlock); return(badBlocks); }
public static async Task <IEnumerable <FileSystemHeaderBlock> > Read( RigidDiskBlock rigidDiskBlock, Stream stream) { if (rigidDiskBlock.FileSysHdrList == BlockIdentifiers.EndOfBlock) { return(Enumerable.Empty <FileSystemHeaderBlock>()); } var fileSystemHeaderBlocks = new List <FileSystemHeaderBlock>(); var fileSysHdrList = rigidDiskBlock.FileSysHdrList; do { // calculate file system header block offset var fileSystemHeaderBlockOffset = rigidDiskBlock.BlockSize * fileSysHdrList; // seek partition block offset stream.Seek(fileSystemHeaderBlockOffset, SeekOrigin.Begin); // read block var block = await BlockHelper.ReadBlock(stream); // parse file system header block var fileSystemHeaderBlock = await Parse(block); fileSystemHeaderBlocks.Add(fileSystemHeaderBlock); // get next partition list block and increase partition number fileSysHdrList = fileSystemHeaderBlock.NextFileSysHeaderBlock; } while (fileSysHdrList > 0 && fileSysHdrList != BlockIdentifiers.EndOfBlock); foreach (var fileSystemHeaderBlock in fileSystemHeaderBlocks) { fileSystemHeaderBlock.LoadSegBlocks = await LoadSegBlockReader.Read(rigidDiskBlock, fileSystemHeaderBlock, stream); } return(fileSystemHeaderBlocks); }
public static RigidDiskBlock Create(long size) { size = size.ToSectorSize(); var rigidDiskBlock = new RigidDiskBlock(); var blocksPerCylinder = rigidDiskBlock.Heads * rigidDiskBlock.Sectors; var cylinderSize = blocksPerCylinder * rigidDiskBlock.BlockSize; var cylinders = (uint)Math.Floor((double)size / cylinderSize); rigidDiskBlock.DiskSize = (long)cylinders * cylinderSize; rigidDiskBlock.Cylinders = cylinders; rigidDiskBlock.ParkingZone = cylinders; rigidDiskBlock.ReducedWrite = cylinders; rigidDiskBlock.WritePreComp = cylinders; var rdbEndOffset = rigidDiskBlock.RdbBlockHi * 512; rigidDiskBlock.LoCylinder = (uint)Math.Ceiling((double)rdbEndOffset / cylinderSize); rigidDiskBlock.HiCylinder = rigidDiskBlock.Cylinders - 1; return(rigidDiskBlock); }
public static PartitionBlock Create(RigidDiskBlock rigidDiskBlock, byte[] dosType, string driveName, long size = 0, bool bootable = false) { var lastPartitionBlock = rigidDiskBlock.PartitionBlocks.LastOrDefault(); var lowCyl = lastPartitionBlock == null ? rigidDiskBlock.LoCylinder : lastPartitionBlock.HighCyl + 1; uint cylinders; if (size > 0) { size = size.ToSectorSize(); var blocksPerCylinder = rigidDiskBlock.Heads * rigidDiskBlock.Sectors; cylinders = (uint)Math.Floor((double)size / (blocksPerCylinder * rigidDiskBlock.BlockSize)); } else { cylinders = lastPartitionBlock == null ? rigidDiskBlock.Cylinders : rigidDiskBlock.Cylinders - lowCyl; } var highCyl = lowCyl + cylinders - 1 > rigidDiskBlock.HiCylinder ? rigidDiskBlock.HiCylinder : lowCyl + cylinders - 1; var partitionBlock = new PartitionBlock { PartitionSize = cylinders * rigidDiskBlock.Heads * rigidDiskBlock.Sectors * rigidDiskBlock.BlockSize, DosType = dosType, DriveName = driveName, Flags = bootable ? (uint)PartitionFlagsEnum.Bootable : 0, LowCyl = lowCyl, HighCyl = highCyl }; return(partitionBlock); }
public static async Task <byte[]> BuildBlock(RigidDiskBlock rigidDiskBlock) { var blockStream = new MemoryStream(rigidDiskBlock.BlockBytes == null || rigidDiskBlock.BlockBytes.Length == 0 ? new byte[BlockSize.RigidDiskBlock * 4] : rigidDiskBlock.BlockBytes); await blockStream.WriteAsciiString(BlockIdentifiers.RigidDiskBlock); await blockStream.WriteLittleEndianUInt32(BlockSize.RigidDiskBlock); // size // skip checksum, calculated when block is built blockStream.Seek(4, SeekOrigin.Current); await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.HostId); // SCSI Target ID of host, not really used await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BlockSize); // Size of disk blocks await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Flags); // RDB Flags await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BadBlockList); // Bad block list await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.PartitionList); // Partition list await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.FileSysHdrList); // File system header list await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.DriveInitCode); // Drive specific init code await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.BootBlockList); // Amiga OS 4 Boot Blocks // read reserved, unused word, need to be set to $ffffffff var reservedBytes = new byte[] { 255, 255, 255, 255 }; for (var i = 0; i < 5; i++) { await blockStream.WriteBytes(reservedBytes); } // physical drive characteristics await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Cylinders); // Number of the cylinders of the drive await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Sectors); // Number of sectors of the drive await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Heads); // Number of heads of the drive await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.Interleave); // Interleave await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.ParkingZone); // Head parking cylinder // read reserved, unused word, need to be set to $ffffffff reservedBytes = new byte[4]; for (var i = 0; i < 3; i++) { await blockStream.WriteBytes(reservedBytes); } await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .WritePreComp); // Starting cylinder of write pre-compensation await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .ReducedWrite); // Starting cylinder of reduced write current await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.StepRate); // Step rate of the drive // read reserved, unused word, need to be set to $ffffffff for (var i = 0; i < 5; i++) { await blockStream.WriteBytes(reservedBytes); } // logical drive characteristics await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .RdbBlockLo); // low block of range reserved for hardblocks await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .RdbBlockHi); // high block of range for these hardblocks await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .LoCylinder); // low cylinder of partitionable disk area await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .HiCylinder); // high cylinder of partitionable data area await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .CylBlocks); // number of blocks available per cylinder await blockStream.WriteLittleEndianUInt32(rigidDiskBlock.AutoParkSeconds); // zero for no auto park await blockStream.WriteLittleEndianUInt32(rigidDiskBlock .HighRsdkBlock); // highest block used by RDSK (not including replacement bad blocks) await blockStream.WriteBytes(reservedBytes); // read reserved, unused word // drive identification await blockStream.WriteString(rigidDiskBlock.DiskVendor, 8, 32); await blockStream.WriteString(rigidDiskBlock.DiskProduct, 16, 32); await blockStream.WriteString(rigidDiskBlock.DiskRevision, 4, 32); // write controller vendor if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerVendor)) { await blockStream.WriteString(rigidDiskBlock.ControllerVendor, 8, 32); } else { await blockStream.WriteBytes(new byte[8]); } // write controller product if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerProduct)) { await blockStream.WriteString(rigidDiskBlock.ControllerProduct, 16, 32); } else { await blockStream.WriteBytes(new byte[16]); } // write controller revision if (!string.IsNullOrWhiteSpace(rigidDiskBlock.ControllerRevision)) { await blockStream.WriteString(rigidDiskBlock.ControllerRevision, 4, 32); } else { await blockStream.WriteBytes(new byte[4]); } await blockStream.WriteBytes(reservedBytes); // read reserved, unused word // calculate and update checksum var blockBytes = blockStream.ToArray(); rigidDiskBlock.Checksum = await BlockHelper.UpdateChecksum(blockBytes, 8); rigidDiskBlock.BlockBytes = blockBytes; return(blockBytes); }
public static async Task WriteBlock(RigidDiskBlock rigidDiskBlock, Stream stream) { // update block pointers to maintain rigid disk block structure BlockHelper.UpdateBlockPointers(rigidDiskBlock); // seek rigid disk block offset stream.Seek(rigidDiskBlock.RdbBlockLo * 512, SeekOrigin.Begin); var rigidDiskBlockBytes = await BuildBlock(rigidDiskBlock); await stream.WriteBytes(rigidDiskBlockBytes); if (rigidDiskBlock.PartitionList != BlockIdentifiers.EndOfBlock) { // seek partition block index offset stream.Seek(rigidDiskBlock.PartitionList * 512, SeekOrigin.Begin); foreach (var partitionBlock in rigidDiskBlock.PartitionBlocks) { var partitionBlockBytes = await PartitionBlockWriter.BuildBlock(partitionBlock); await stream.WriteBytes(partitionBlockBytes); if (partitionBlock.NextPartitionBlock == BlockIdentifiers.EndOfBlock) { break; } // seek next partition block index offset stream.Seek(partitionBlock.NextPartitionBlock * 512, SeekOrigin.Begin); } } if (rigidDiskBlock.FileSysHdrList != BlockIdentifiers.EndOfBlock) { // seek file system header block index offset stream.Seek(rigidDiskBlock.FileSysHdrList * 512, SeekOrigin.Begin); foreach (var fileSystemHeaderBlock in rigidDiskBlock.FileSystemHeaderBlocks) { var fileSystemHeaderBytes = await FileSystemHeaderBlockWriter.BuildBlock(fileSystemHeaderBlock); await stream.WriteBytes(fileSystemHeaderBytes); // seek load seg block index offset stream.Seek(fileSystemHeaderBlock.SegListBlocks * 512, SeekOrigin.Begin); foreach (var loadSegBlock in fileSystemHeaderBlock.LoadSegBlocks) { var loadSegBlockBytes = await LoadSegBlockWriter.BuildBlock(loadSegBlock); await stream.WriteBytes(loadSegBlockBytes); if (loadSegBlock.NextLoadSegBlock == -1) { break; } // seek next load seg block index offset stream.Seek(loadSegBlock.NextLoadSegBlock * 512, SeekOrigin.Begin); } if (fileSystemHeaderBlock.NextFileSysHeaderBlock == BlockIdentifiers.EndOfBlock) { break; } // seek next file system header block index offset stream.Seek(fileSystemHeaderBlock.NextFileSysHeaderBlock * 512, SeekOrigin.Begin); } } if (rigidDiskBlock.BadBlockList != BlockIdentifiers.EndOfBlock) { // seek bad block index offset stream.Seek(rigidDiskBlock.BadBlockList * 512, SeekOrigin.Begin); foreach (var badBlock in rigidDiskBlock.BadBlocks) { var badBlockBytes = await BadBlockWriter.BuildBlock(badBlock); await stream.WriteBytes(badBlockBytes); if (badBlock.NextBadBlock == BlockIdentifiers.EndOfBlock) { break; } // seek next bad block index offset stream.Seek(badBlock.NextBadBlock * 512, SeekOrigin.Begin); } } }
public static async Task <PartitionBlock> Parse(RigidDiskBlock rigidDiskBlock, byte[] blockBytes) { var blockStream = new MemoryStream(blockBytes); var magic = await blockStream.ReadAsciiString(); // Identifier 32 bit word : 'PART' if (!magic.Equals(BlockIdentifiers.PartitionBlock)) { return(null); } await blockStream.ReadUInt32(); // Size of the structure for checksums var checksum = await blockStream.ReadInt32(); // Checksum of the structure var hostId = await blockStream.ReadUInt32(); // SCSI Target ID of host, not really used var nextPartitionBlock = await blockStream.ReadUInt32(); // Block number of the next PartitionBlock var flags = await blockStream.ReadUInt32(); // Part Flags (NOMOUNT and BOOTABLE) // skip reserved blockStream.Seek(4 * 2, SeekOrigin.Current); var devFlags = await blockStream.ReadUInt32(); // Preferred flags for OpenDevice var driveNameLength = (await blockStream.ReadBytes(1)).FirstOrDefault(); // Preferred DOS device name: BSTR form var driveName = await blockStream.ReadString(driveNameLength); // # Preferred DOS device name: BSTR form if (driveNameLength < 31) { await blockStream.ReadBytes(31 - driveNameLength); } // skip reserved blockStream.Seek(4 * 15, SeekOrigin.Current); var sizeOfVector = await blockStream.ReadUInt32(); // Size of Environment vector var sizeBlock = await blockStream.ReadUInt32(); // Size of the blocks in 32 bit words, usually 128 var secOrg = await blockStream.ReadUInt32(); // Not used; must be 0 var surfaces = await blockStream.ReadUInt32(); // Number of heads (surfaces) var sectors = await blockStream.ReadUInt32(); // Disk sectors per block, used with SizeBlock, usually 1 var blocksPerTrack = await blockStream.ReadUInt32(); // Blocks per track. drive specific var reserved = await blockStream.ReadUInt32(); // DOS reserved blocks at start of partition. var preAlloc = await blockStream.ReadUInt32(); // DOS reserved blocks at end of partition var interleave = await blockStream.ReadUInt32(); // Not used, usually 0 var lowCyl = await blockStream.ReadUInt32(); // First cylinder of the partition var highCyl = await blockStream.ReadUInt32(); // Last cylinder of the partition var numBuffer = await blockStream.ReadUInt32(); // Initial # DOS of buffers. var bufMemType = await blockStream.ReadUInt32(); // Type of mem to allocate for buffers var maxTransfer = await blockStream.ReadUInt32(); // Max number of bytes to transfer at a time var mask = await blockStream.ReadUInt32(); // Address Mask to block out certain memory var bootPriority = await blockStream.ReadUInt32(); // Boot priority for autoboot var dosType = await blockStream.ReadBytes(4); // # Dostype of the file system var baud = await blockStream.ReadUInt32(); // Baud rate for serial handler var control = await blockStream.ReadUInt32(); // Control word for handler/filesystem var bootBlocks = await blockStream.ReadUInt32(); // Number of blocks containing boot code // skip reserved blockStream.Seek(4 * 12, SeekOrigin.Current); // calculate size of partition in bytes var partitionSize = (long)(highCyl - lowCyl + 1) * surfaces * blocksPerTrack * rigidDiskBlock.BlockSize; var calculatedChecksum = await BlockHelper.CalculateChecksum(blockBytes, 8); if (checksum != calculatedChecksum) { throw new Exception("Invalid partition block checksum"); } var fileSystemBlockSize = sizeBlock * 4; return(new PartitionBlock { BlockBytes = blockBytes, Checksum = checksum, HostId = hostId, NextPartitionBlock = nextPartitionBlock, Flags = flags, DevFlags = devFlags, DriveName = driveName, SizeOfVector = sizeOfVector, SizeBlock = sizeBlock, SecOrg = secOrg, Surfaces = surfaces, Sectors = sectors, BlocksPerTrack = blocksPerTrack, Reserved = reserved, PreAlloc = preAlloc, Interleave = interleave, LowCyl = lowCyl, HighCyl = highCyl, NumBuffer = numBuffer, BufMemType = bufMemType, MaxTransfer = maxTransfer, Mask = mask, BootPriority = bootPriority, DosType = dosType, Baud = baud, Control = control, BootBlocks = bootBlocks, PartitionSize = partitionSize, FileSystemBlockSize = fileSystemBlockSize, }); }
/// <summary> /// update block pointers to maintain rigid disk block structure. required when changes to rigid disk block needs block pointers updated like adding or deleting partition blocks /// </summary> /// <param name="rigidDiskBlock"></param> public static void UpdateBlockPointers(RigidDiskBlock rigidDiskBlock) { var highRsdkBlock = rigidDiskBlock.RdbBlockLo; var partitionBlocks = rigidDiskBlock.PartitionBlocks.ToList(); var rigidDiskBlockIndex = rigidDiskBlock.RdbBlockLo; var partitionBlockIndex = partitionBlocks.Count > 0 ? rigidDiskBlockIndex + 1 : BlockIdentifiers.EndOfBlock; var partitionsChanged = rigidDiskBlock.PartitionList != partitionBlockIndex; rigidDiskBlock.PartitionList = partitionBlockIndex; for (var p = 0; p < partitionBlocks.Count; p++) { var partitionBlock = partitionBlocks[p]; var nextPartitionBlock = p < partitionBlocks.Count - 1 ? (uint)(partitionBlockIndex + p + 1) : BlockIdentifiers.EndOfBlock; if (partitionBlock.NextPartitionBlock != nextPartitionBlock) { partitionsChanged = true; } partitionBlock.NextPartitionBlock = nextPartitionBlock; if (partitionBlockIndex + p > highRsdkBlock) { highRsdkBlock = (uint)(partitionBlockIndex + p); } } if (partitionsChanged) { ResetFileSystemHeaderBlockPointers(rigidDiskBlock); ResetBadBlockPointers(rigidDiskBlock); } var fileSystemHeaderBlocks = rigidDiskBlock.FileSystemHeaderBlocks.ToList(); var fileSystemHeaderBlockIndex = fileSystemHeaderBlocks.Count > 0 ? highRsdkBlock + 1 : BlockIdentifiers.EndOfBlock; var fileSystemHeaderChanged = rigidDiskBlock.FileSysHdrList != fileSystemHeaderBlockIndex; if (fileSystemHeaderChanged) { ResetBadBlockPointers(rigidDiskBlock); } rigidDiskBlock.FileSysHdrList = fileSystemHeaderBlockIndex; for (var f = 0; f < fileSystemHeaderBlocks.Count; f++) { var fileSystemHeaderBlock = fileSystemHeaderBlocks[f]; var loadSegBlocks = fileSystemHeaderBlock.LoadSegBlocks.ToList(); fileSystemHeaderBlock.NextFileSysHeaderBlock = f < fileSystemHeaderBlocks.Count - 1 ? (uint)(fileSystemHeaderBlockIndex + f + 1 + loadSegBlocks.Count) : BlockIdentifiers.EndOfBlock; fileSystemHeaderBlock.SegListBlocks = (int)(fileSystemHeaderBlockIndex + f + 1); if (fileSystemHeaderBlockIndex + f + loadSegBlocks.Count > highRsdkBlock) { highRsdkBlock = (uint)(fileSystemHeaderBlockIndex + f + loadSegBlocks.Count); } for (var l = 0; l < loadSegBlocks.Count; l++) { var loadSegBlock = loadSegBlocks[l]; loadSegBlock.NextLoadSegBlock = l < loadSegBlocks.Count - 1 ? (int)(fileSystemHeaderBlockIndex + f + 2 + l) : -1; } } var badBlocks = rigidDiskBlock.BadBlocks.ToList(); var badBlockIndex = badBlocks.Count > 0 ? highRsdkBlock + 1 : BlockIdentifiers.EndOfBlock; rigidDiskBlock.BadBlockList = badBlockIndex; for (var b = 0; b < badBlocks.Count; b++) { var badBlock = badBlocks[b]; badBlock.NextBadBlock = b < badBlocks.Count - 1 ? (uint)(badBlockIndex + b + 1) : BlockIdentifiers.EndOfBlock; // update highest used rdb block if (badBlockIndex + b > highRsdkBlock) { highRsdkBlock = (uint)(badBlockIndex + b); } } // set highest used rdb block rigidDiskBlock.HighRsdkBlock = highRsdkBlock; }
public static void ResetRigidDiskBlockPointers(RigidDiskBlock rigidDiskBlock) { ResetPartitionBlockPointers(rigidDiskBlock); ResetFileSystemHeaderBlockPointers(rigidDiskBlock); ResetBadBlockPointers(rigidDiskBlock); }