private int GetPartitionOffset(int index)
        {
            bool found        = false;
            int  entriesSoFar = 0;
            int  position     = 0;

            while (!found && position < _primaryHeader.PartitionEntryCount)
            {
                GptEntry entry = new GptEntry();
                entry.ReadFrom(_entryBuffer, position * _primaryHeader.PartitionEntrySize);
                if (entry.PartitionType != Guid.Empty)
                {
                    if (index == entriesSoFar)
                    {
                        found = true;
                        break;
                    }

                    entriesSoFar++;
                }

                position++;
            }

            if (found)
            {
                return(position * _primaryHeader.PartitionEntrySize);
            }
            throw new IOException(string.Format(CultureInfo.InvariantCulture, "No such partition: {0}", index));
        }
        internal SparseStream Open(GptEntry entry)
        {
            long start = entry.FirstUsedLogicalBlock * _diskGeometry.BytesPerSector;
            long end   = (entry.LastUsedLogicalBlock + 1) * _diskGeometry.BytesPerSector;

            return(new SubStream(_diskData, start, end - start));
        }
        private void EstablishReservedPartition(List <GptEntry> allEntries)
        {
            // If no MicrosoftReserved partition, and no Microsoft Data partitions, and the disk
            // has a 'reasonable' size free, create a Microsoft Reserved partition.
            if (CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.MicrosoftReserved) == 0 &&
                CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.WindowsBasicData) == 0 &&
                _diskGeometry.Capacity > 512 * 1024 * 1024)
            {
                long reservedStart = FirstAvailableSector(allEntries);
                long reservedEnd   = FindLastFreeSector(reservedStart, allEntries);

                if ((reservedEnd - reservedStart + 1) * _diskGeometry.BytesPerSector > 512 * 1024 * 1024)
                {
                    long size = (_diskGeometry.Capacity < 16 * 1024L * 1024 * 1024 ? 32 : 128) * 1024 * 1024;
                    reservedEnd = reservedStart + size / _diskGeometry.BytesPerSector - 1;

                    int      reservedOffset   = GetFreeEntryOffset();
                    GptEntry newReservedEntry = new GptEntry();
                    newReservedEntry.PartitionType         = GuidPartitionTypes.MicrosoftReserved;
                    newReservedEntry.Identity              = Guid.NewGuid();
                    newReservedEntry.FirstUsedLogicalBlock = reservedStart;
                    newReservedEntry.LastUsedLogicalBlock  = reservedEnd;
                    newReservedEntry.Attributes            = 0;
                    newReservedEntry.Name = "Microsoft reserved partition";
                    newReservedEntry.WriteTo(_entryBuffer, reservedOffset);
                    allEntries.Add(newReservedEntry);
                }
            }
        }
 private IEnumerable <GptEntry> GetAllEntries()
 {
     for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
     {
         GptEntry entry = new GptEntry();
         entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);
         if (entry.PartitionType != Guid.Empty)
         {
             yield return(entry);
         }
     }
 }
        /// <summary>
        /// Creates a new partition that encompasses the entire disk.
        /// </summary>
        /// <param name="type">The partition type</param>
        /// <param name="active">Whether the partition is active (bootable)</param>
        /// <returns>The index of the partition</returns>
        /// <remarks>The partition table must be empty before this method is called,
        /// otherwise IOException is thrown.</remarks>
        public override int Create(WellKnownPartitionType type, bool active)
        {
            List <GptEntry> allEntries = new List <GptEntry>(GetAllEntries());

            // If no MicrosoftReserved partition, and no Microsoft Data partitions, and the disk
            // has a 'reasonable' size free, create a Microsoft Reserved partition.
            if (CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.MicrosoftReserved) == 0 &&
                CountEntries(allEntries, e => e.PartitionType == GuidPartitionTypes.WindowsBasicData) == 0 &&
                _diskGeometry.Capacity > 512 * 1024 * 1024)
            {
                long reservedStart = FirstAvailableSector(allEntries);
                long reservedEnd   = FindLastFreeSector(reservedStart, allEntries);

                if ((reservedEnd - reservedStart + 1) * _diskGeometry.BytesPerSector > 512 * 1024 * 1024)
                {
                    long size = ((_diskGeometry.Capacity < (16 * 1024L * 1024 * 1024)) ? 32 : 128) * 1024 * 1024;
                    reservedEnd = reservedStart + (size / _diskGeometry.BytesPerSector) - 1;

                    int      reservedOffset   = GetFreeEntryOffset();
                    GptEntry newReservedEntry = new GptEntry();
                    newReservedEntry.PartitionType         = GuidPartitionTypes.MicrosoftReserved;
                    newReservedEntry.Identity              = Guid.NewGuid();
                    newReservedEntry.FirstUsedLogicalBlock = reservedStart;
                    newReservedEntry.LastUsedLogicalBlock  = reservedEnd;
                    newReservedEntry.Attributes            = 0;
                    newReservedEntry.Name = "Microsoft reserved partition";
                    newReservedEntry.WriteTo(_entryBuffer, reservedOffset);
                    allEntries.Add(newReservedEntry);
                }
            }

            // Fill the rest of the disk with the requested partition
            long start = FirstAvailableSector(allEntries);
            long end   = FindLastFreeSector(start, allEntries);

            int      offset   = GetFreeEntryOffset();
            GptEntry newEntry = new GptEntry();

            newEntry.PartitionType         = GuidPartitionTypes.Convert(type);
            newEntry.Identity              = Guid.NewGuid();
            newEntry.FirstUsedLogicalBlock = start;
            newEntry.LastUsedLogicalBlock  = end;
            newEntry.Attributes            = 0;
            newEntry.Name = "Data Partition";
            newEntry.WriteTo(_entryBuffer, offset);

            // Commit changes to disk
            Write();

            return(GetEntryIndex(newEntry.Identity));
        }
        private int GetFreeEntryOffset()
        {
            for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
            {
                GptEntry entry = new GptEntry();
                entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);

                if (entry.PartitionType == Guid.Empty)
                {
                    return(i * _primaryHeader.PartitionEntrySize);
                }
            }

            throw new IOException("No free partition entries available");
        }
        /// <summary>
        /// Makes a best guess at the geometry of a disk.
        /// </summary>
        /// <param name="disk">String containing the disk image to detect the geometry from.</param>
        /// <returns>The detected geometry.</returns>
        public static Geometry DetectGeometry(Stream disk)
        {
            if (disk.Length >= Sizes.Sector)
            {
                disk.Position = 0;
                byte[] bootSector = StreamUtilities.ReadExact(disk, Sizes.Sector);
                if (bootSector[510] == 0x55 && bootSector[511] == 0xAA)
                {
                    long lastSector = 0;

                    disk.Position = Sizes.Sector;
                    var sector = StreamUtilities.ReadExact(disk, Sizes.Sector);
                    var header = new GptHeader(Sizes.Sector);
                    if (!header.ReadFrom(sector, 0))
                    {
                        throw new InvalidDataException("Failed to read primary GPT header");
                    }

                    disk.Position = header.PartitionEntriesLba * Sizes.Sector;
                    var entryBuffer = StreamUtilities.ReadExact(disk,
                                                                (int)(header.PartitionEntrySize * header.PartitionEntryCount));

                    if (header.EntriesCrc != Crc32LittleEndian.Compute(Crc32Algorithm.Common,
                                                                       entryBuffer, 0, entryBuffer.Length))
                    {
                        throw new InvalidDataException("Invalid GPT header");
                    }

                    for (int i = 0; i < header.PartitionEntryCount; ++i)
                    {
                        GptEntry entry = new GptEntry();
                        entry.ReadFrom(entryBuffer, i * header.PartitionEntrySize);

                        if (entry.PartitionType != Guid.Empty)
                        {
                            lastSector = entry.LastUsedLogicalBlock + 1;
                        }
                    }

                    if (lastSector > 0)
                    {
                        return(Geometry.FromCapacity(lastSector * Sizes.Sector, Sizes.Sector));
                    }
                }
            }

            return(Geometry.FromCapacity(disk.Length));
        }
        private int GetEntryIndex(Guid identity)
        {
            int index = 0;

            for (int i = 0; i < _primaryHeader.PartitionEntryCount; ++i)
            {
                GptEntry entry = new GptEntry();
                entry.ReadFrom(_entryBuffer, i * _primaryHeader.PartitionEntrySize);

                if (entry.Identity == identity)
                {
                    return(index);
                }
                if (entry.PartitionType != Guid.Empty)
                {
                    index++;
                }
            }

            throw new IOException("No such partition");
        }
        private GptEntry CreateEntry(long startSector, long endSector, Guid type, long attributes, string name)
        {
            if (endSector < startSector)
            {
                throw new ArgumentException("The end sector is before the start sector");
            }

            int      offset   = GetFreeEntryOffset();
            GptEntry newEntry = new GptEntry();

            newEntry.PartitionType         = type;
            newEntry.Identity              = Guid.NewGuid();
            newEntry.FirstUsedLogicalBlock = startSector;
            newEntry.LastUsedLogicalBlock  = endSector;
            newEntry.Attributes            = (ulong)attributes;
            newEntry.Name = name;
            newEntry.WriteTo(_entryBuffer, offset);

            // Commit changes to disk
            Write();

            return(newEntry);
        }
        /// <summary>
        /// Creates a new GUID partition on the disk.
        /// </summary>
        /// <param name="startSector">The first sector of the partition.</param>
        /// <param name="endSector">The last sector of the partition.</param>
        /// <param name="type">The partition type.</param>
        /// <param name="attributes">The partition attributes.</param>
        /// <param name="name">The name of the partition.</param>
        /// <returns>The index of the new partition.</returns>
        /// <remarks>No checking is performed on the parameters, the caller is
        /// responsible for ensuring that the partition does not overlap other partitions.</remarks>
        public int Create(long startSector, long endSector, Guid type, long attributes, string name)
        {
            GptEntry newEntry = CreateEntry(startSector, endSector, type, attributes, name);

            return(GetEntryIndex(newEntry.Identity));
        }
 internal SparseStream Open(GptEntry entry)
 {
     return(new SubStream(_diskData, entry.FirstUsedLogicalBlock * _diskGeometry.BytesPerSector, entry.LastUsedLogicalBlock * _diskGeometry.BytesPerSector));
 }
 internal GuidPartitionInfo(GuidPartitionTable table, GptEntry entry)
 {
     _table = table;
     _entry = entry;
 }