Example #1
0
        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);
            }
        }
Example #2
0
        /// <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);
        }
Example #3
0
        /// <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);
        }