Example #1
0
        //Formats a volume with FAT12 file system - currently assumes it's a floppy disk...
        public static Fat12FileSystem Create(Stream stream, BiosParameterBlock bpb, bool writeBPB)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream), "stream cannot be null!");
            }
            if (bpb == null)
            {
                throw new ArgumentNullException(nameof(bpb), "bpb cannot be null!");
            }
            if (!bpb.Validate())
            {
                throw new InvalidDataException("bpb is invalid!");
            }

            var fat = new Fat12FileSystem(stream, bpb);

            uint totalSize      = (uint)stream.Length;
            uint rootDirSize    = (uint)(bpb.RootDirectoryEntries << 5);
            uint fatSize        = (uint)(bpb.LogicalSectorsPerFAT * bpb.BytesPerLogicalSector);
            uint fat1Offset     = (uint)(bpb.ReservedLogicalSectors * bpb.BytesPerLogicalSector);
            uint fat2Offset     = fat1Offset + fatSize;
            uint dataAreaOffset = fat2Offset + fatSize + rootDirSize;

            using (var writer = new BinaryWriter(stream, Encoding.ASCII, true))
            {
                stream.Seek(0, SeekOrigin.Begin);
                for (uint i = 0; i < totalSize; i++)
                {
                    writer.Write((byte)0);
                }

                //Fille the data area with 0xF6
                stream.Seek(dataAreaOffset, SeekOrigin.Begin);
                for (uint i = 0; i < totalSize - dataAreaOffset; i++)
                {
                    writer.Write((byte)0xF6);
                }

                stream.Seek(0, SeekOrigin.Begin);
                writer.Write(bpb.BootJump, 0, 3);
                writer.Write(bpb.OemId.PadRight(8, ' ').ToCharArray());

                if (writeBPB)
                {
                    writer.Write(bpb.BytesPerLogicalSector);
                    writer.Write(bpb.LogicalSectorsPerCluster);
                    writer.Write(bpb.ReservedLogicalSectors);
                    writer.Write(bpb.NumberOfFATs);
                    writer.Write(bpb.RootDirectoryEntries);
                    writer.Write(bpb.TotalLogicalSectors <= ushort.MaxValue ? (ushort)bpb.TotalLogicalSectors : (ushort)0);
                    writer.Write(bpb.MediaDescriptor);
                    writer.Write(bpb.Version != BiosParameterBlockVersion.Fat32 ? (ushort)bpb.LogicalSectorsPerFAT : (ushort)0);
                    writer.Write(bpb.PhysicalSectorsPerTrack);
                    writer.Write(bpb.NumberOfHeads);
                    writer.Write(bpb.HiddenSectors);
                    writer.Write(bpb.TotalLogicalSectors > ushort.MaxValue ? bpb.TotalLogicalSectors : 0);

                    //DOS 3.4+ specific values
                    if (bpb.Version == BiosParameterBlockVersion.Dos34 || bpb.Version == BiosParameterBlockVersion.Dos40)
                    {
                        var ebpb = (ExtendedBiosParameterBlock)bpb;
                        writer.Write(ebpb.PhysicalDriveNumber);
                        writer.Write(ebpb.Flags);
                        writer.Write((byte)ebpb.ExtendedBootSignature);
                        writer.Write(ebpb.VolumeSerialNumber.Value);

                        //DOS 4.0 adds volume label and FS type as well
                        if (bpb.Version == BiosParameterBlockVersion.Dos40)
                        {
                            if (string.IsNullOrWhiteSpace(ebpb.VolumeLabel))
                            {
                                writer.Write("NO NAME    ".ToCharArray());
                            }
                            else
                            {
                                writer.Write(ebpb.VolumeLabel.PadRight(11, ' ').ToCharArray());
                            }
                            writer.Write(ebpb.FileSystemType.PadRight(8, ' ').ToCharArray());
                        }
                    }
                }

                //Boot signature
                stream.Seek(0x1FE, SeekOrigin.Begin);
                writer.Write((byte)0x55);
                writer.Write((byte)0xAA);

                /* Media descriptor needs to be written to each FAT as well, upper 4 bits must all be set.
                 * It takes up the first cluster entry (0), and the second entry (1) is also reserved */
                stream.Seek(fat1Offset, SeekOrigin.Begin);
                writer.Write(bpb.MediaDescriptor);
                writer.Write((byte)0xFF);
                writer.Write((byte)0xFF);
                stream.Seek(fat2Offset, SeekOrigin.Begin);
                writer.Write(bpb.MediaDescriptor);
                writer.Write((byte)0xFF);
                writer.Write((byte)0xFF);

                //Volume label needs to be written to the root directory as well
                stream.Seek(fat2Offset + fatSize, SeekOrigin.Begin);

                //First 11 bytes (8.3 space-padded filename without the period) are the label itself
                {
                    if (bpb is ExtendedBiosParameterBlock bpb40 && !string.IsNullOrEmpty(bpb40.VolumeLabel))
                    {
                        writer.Write(bpb40.VolumeLabel.PadRight(11, ' ').ToCharArray());
                        writer.Write((byte)0x08); //Volume label attribute
                    }
                }
            }

            return(fat);
        }