private ExFatPartition(Stream partitionStream, ExFatOptions options, bool readBootsector) { if (!partitionStream.CanSeek) { throw new ArgumentException("Given stream must be seekable"); } if (!partitionStream.CanRead) { throw new ArgumentException("Given stream must be readable"); } _partitionStream = partitionStream; _options = options; if (readBootsector) { BootSector = ReadBootSector(_partitionStream); } }
/// <summary> /// Reads the boot sector. /// </summary> /// <param name="partitionStream">The partition stream.</param> /// <returns></returns> public static ExFatBootSector ReadBootSector(Stream partitionStream) { partitionStream.Seek(0, SeekOrigin.Begin); var defaultBootSector = new ExFatBootSector(new byte[512]); defaultBootSector.Read(partitionStream); var sectorSize = defaultBootSector.BytesPerSector.Value; // it probably not a valid exFAT boot sector, so don't dig any further if (sectorSize < 512 || sectorSize > 4096) { return(defaultBootSector); } var fullData = new byte[sectorSize * 12]; partitionStream.Seek(0, SeekOrigin.Begin); var bootSector = new ExFatBootSector(fullData); bootSector.Read(partitionStream); return(bootSector); }
/// <summary> /// Formats the specified partition stream. /// </summary> /// <param name="partitionStream">The partition stream.</param> /// <param name="options">The options.</param> /// <param name="volumeLabel">The volume label.</param> /// <returns></returns> public static ExFatPartition Format(Stream partitionStream, ExFatFormatOptions options, string volumeLabel = null) { var partition = new ExFatPartition(partitionStream, 0, false); partitionStream.Seek(0, SeekOrigin.Begin); var volumeSpace = options?.VolumeSpace ?? (ulong)partitionStream.Length; var bytesPerSector = options?.BytesPerSector ?? 512; var totalSectors = volumeSpace / bytesPerSector; var sectorsPerCluster = options?.SectorsPerCluster ?? ComputeSectorsPerCluster(totalSectors); if (sectorsPerCluster > 1 << 25) { throw new ArgumentException("Sectors per cluster can not exceed 2^25"); } const uint fats = 1; const uint usedFats = fats; const uint bootSectors = 12; const uint bpbSectors = 2 * bootSectors; // create bootsector var bootSectorBytes = new byte[bytesPerSector * 12]; var bootSector = new ExFatBootSector(bootSectorBytes); var totalClusters = (uint)(totalSectors - bpbSectors) / (usedFats * 4 / bytesPerSector + sectorsPerCluster); var sectorsPerFat = totalClusters * 4 / bytesPerSector; bootSector.JmpBoot.Set(ExFatBootSector.DefaultJmpBoot); bootSector.OemName.Value = ExFatBootSector.ExFatOemName; bootSector.VolumeLengthSectors.Value = totalSectors; bootSector.FatOffsetSector.Value = Align(bpbSectors, bytesPerSector); bootSector.FatLengthSectors.Value = Align(sectorsPerFat, bytesPerSector); bootSector.ClusterOffsetSector.Value = Align(bootSector.FatOffsetSector.Value + usedFats * bootSector.FatLengthSectors.Value, bytesPerSector); totalClusters = (uint)((volumeSpace / bytesPerSector - bootSector.ClusterOffsetSector.Value) / sectorsPerCluster); if (totalClusters > 0xFFFFFFF0) { throw new ArgumentException("clusters are too small to address full disk"); } bootSector.ClusterCount.Value = totalClusters; bootSector.VolumeSerialNumber.Value = (uint)new Random().Next(); bootSector.FileSystemRevision.Value = 256; bootSector.VolumeFlags.Value = 0; bootSector.BytesPerSector.Value = bytesPerSector; bootSector.SectorsPerCluster.Value = sectorsPerCluster; bootSector.NumberOfFats.Value = (byte)fats; bootSector.DriveSelect.Value = 0x80; bootSector.PercentInUse.Value = 0xFF; for (int sectorIndex = 0; sectorIndex <= 8; sectorIndex++) { bootSectorBytes[sectorIndex * bytesPerSector + bytesPerSector - 2] = 0x55; bootSectorBytes[sectorIndex * bytesPerSector + bytesPerSector - 1] = 0xAA; } partition.BootSector = bootSector; // prepare FAT partition.ClearFat(); partition.SetNextCluster(0, Cluster.Marker); partition.SetNextCluster(1, Cluster.Last); // create allocation bitmap var allocationBitmapEntry = CreateAllocationBitmap(partition, totalClusters); // create root directory var directoryDataDescriptor = new DataDescriptor(0, false, ulong.MaxValue, ulong.MaxValue); using (var s = partition.OpenClusterStream(directoryDataDescriptor, FileAccess.ReadWrite, d => directoryDataDescriptor = new DataDescriptor(d.FirstCluster, d.Contiguous, long.MaxValue, long.MaxValue))) { s.SetDataLength(bytesPerSector * sectorsPerCluster); } bootSector.RootDirectoryCluster.Value = directoryDataDescriptor.FirstCluster.ToUInt32(); // boot sector is now complete var checksum = bootSector.ComputeChecksum(); for (var offset = 11 * bytesPerSector; offset < 12 * bytesPerSector; offset += 4) { bootSectorBytes[offset] = checksum[0]; bootSectorBytes[offset + 1] = checksum[1]; bootSectorBytes[offset + 2] = checksum[2]; bootSectorBytes[offset + 3] = checksum[3]; } partition.WriteSectors(0, bootSectorBytes, (int)bootSectors); partition.WriteSectors(bootSectors, bootSectorBytes, (int)bootSectors); using (var directoryStream = partition.OpenClusterStream(directoryDataDescriptor, FileAccess.ReadWrite)) { allocationBitmapEntry.Write(directoryStream); CreateUpCaseTable(partition, directoryStream); CreateVolumeLabel(directoryStream, volumeLabel); } partition.Flush(); return(partition); }