Skip to content

Commit

Permalink
fix: add partition GUID for Linux, Windows, Mac
Browse files Browse the repository at this point in the history
Add additional partition UUID values for common OS's
  • Loading branch information
swysocki committed Sep 10, 2022
1 parent 485b90d commit e45c102
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 35 deletions.
4 changes: 2 additions & 2 deletions gpt_image/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pathlib

from gpt_image.geometry import Geometry
from gpt_image.partition import Partition, PartitionEntryArray
from gpt_image.partition import Partition, PartitionEntryArray, PartitionType
from gpt_image.table import Header, Table


Expand Down Expand Up @@ -75,7 +75,7 @@ def open(self) -> None:
offset : offset + PartitionEntryArray.EntryLength
]
new_part = Partition.unmarshal(partition_bytes, self.geometry.sector_size)
if new_part.type_guid != Partition._EMPTY_GUID:
if new_part.type_guid != PartitionType.UNUSED.value:
self.table.partitions.entries.append(new_part)

def __repr__(self) -> str:
Expand Down
53 changes: 48 additions & 5 deletions gpt_image/partition.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import struct
import uuid
from enum import IntEnum
from enum import Enum, IntEnum
from math import ceil
from typing import List

Expand Down Expand Up @@ -29,6 +29,53 @@ class PartitionAttribute(IntEnum):
NO_DRIVE_LETTER = 63


class PartitionType(Enum):
# https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries

UNUSED = "00000000-0000-0000-0000-000000000000"
# Common Linux
LINUX_FILE_SYSTEM = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
EFI_SYSTEM_PARTITION = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
RAID_PARTITION = "A19D880F-05FC-4D3B-A006-743F0F84911E"
ROOT_PARTITION_X86 = "44479540-F297-41B2-9AF7-D131D5F0458A"
ROOT_PARTITION_X86_64 = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709"
ROOT_PARTITION_ARM = "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3"
ROOT_PARTITION_ARM_64 = "B921B045-1DF0-41C3-AF44-4C6F280D3FAE"
BOOT_PARTITION = "BC13C2FF-59E6-4262-A352-B275FD6F7172"
SWAP_PARTITION = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F"
LOGICAL_VOLUME_MANAGER_PARTITION = "E6D6D379-F507-44C2-A23C-238F2A3DF928"
HOME_PARTITION = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915"
SRV_SERVER_DATA_PARTITION = "3B8F8425-20E0-4F3B-907F-1A25A76F98E8"
PLAIN_DMCRYPT_PARTITION = "7FFEC5C9-2D00-49B7-8941-3EA10A5586B7"
LUKS_PARTITION = "CA7D7CCB-63ED-4C53-861C-1742536059CC"

# Common Mac
HIERARCHICAL_FILE_SYSTEM_PLUS_PARTITION = "48465300-0000-11AA-AA11-00306543ECAC"
APPLE_APFS_CONTAINER = "7C3457EF-0000-11AA-AA11-00306543ECAC"
APFS_FILEVAULT_VOLUME_CONTAINER = "7C3457EF-0000-11AA-AA11-00306543ECAC"
APPLE_UFS_CONTAINER = "55465300-0000-11AA-AA11-00306543ECAC"
ZFS = "6A898CC3-1DD2-11B2-99A6-080020736631"
APPLE_RAID_PARTITION = "52414944-0000-11AA-AA11-00306543ECAC"
APPLE_RAID_PARTITION_OFFLINE = "52414944-5F4F-11AA-AA11-00306543ECAC"
APPLE_BOOT_PARTITION_RECOVERY_HD = "426F6F74-0000-11AA-AA11-00306543ECAC"
APPLE_LABEL = "4C616265-6C00-11AA-AA11-00306543ECAC"
APPLE_TV_RECOVERY_PARTITION = "5265636F-7665-11AA-AA11-00306543ECAC"
APPLE_CORE_STORAGE_CONTAINER = "53746F72-6167-11AA-AA11-00306543ECAC"
HFS_FILEVAULT_VOLUME_CONTAINER = "53746F72-6167-11AA-AA11-00306543ECAC"
APPLE_APFS_PREBOOT_PARTITION = "69646961-6700-11AA-AA11-00306543ECAC"
APPLE_APFS_RECOVERY_PARTITION = "52637672-7900-11AA-AA11-00306543ECAC"

# Common Windows
MICROSOFT_RESERVED_PARTITION = "E3C9E316-0B5C-4DB8-817D-F92DF00215AE"
BASIC_DATA_PARTITION = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"
LOGICAL_DISK_MANAGER_METADATA_PARTITION = "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3"
LOGICAL_DISK_MANAGER_DATA_PARTITION = "AF9B60A0-1431-4F62-BC68-3311714A69AD"
WINDOWS_RECOVERY_ENVIRONMENT = "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC"
IBM_GENERAL_PARALLEL_FILE_SYSTEM_PARTITION = "37AFFC90-EF7D-4E96-91C3-2D7AE055B174"
STORAGE_SPACES_PARTITION = "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D"
STORAGE_REPLICA_PARTITION = "558D43C5-A1AC-43C0-AAC8-D1472B2923D1"


class Partition:
"""Partition class represents a GPT partition
Expand All @@ -48,10 +95,6 @@ class Partition:
"""

_PARTITION_FORMAT = struct.Struct("<16s16sQQQ72s")
_EMPTY_GUID = "00000000-0000-0000-0000-000000000000"
# https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries
LINUX_FILE_SYSTEM = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
EFI_SYSTEM_PARTITION = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"

def __init__(
self,
Expand Down
6 changes: 3 additions & 3 deletions tests/test_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from gpt_image.disk import Disk
from gpt_image.partition import Partition
from gpt_image.partition import Partition, PartitionType

BYTE_DATA = b"\x01\x02\x03\x04"
DISK_SIZE = 4 * 1024 * 1024 # 4 MB
Expand All @@ -14,8 +14,8 @@ def new_image(tmp_path):
image_name = tmp_path / "test.img"
disk = Disk(image_name)
disk.create(DISK_SIZE)
part1 = Partition("partition1", 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part2 = Partition("partition2", 3 * 1024, Partition.LINUX_FILE_SYSTEM)
part1 = Partition("partition1", 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part2 = Partition("partition2", 3 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
disk.table.partitions.add(part1)
disk.table.partitions.add(part2)
disk.commit()
Expand Down
10 changes: 6 additions & 4 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import shutil
import subprocess
import sys
import uuid

import pytest

from gpt_image import disk
from gpt_image.partition import Partition, PartitionAttribute
from gpt_image.partition import Partition, PartitionAttribute, PartitionType

STATIC_UUID = "4133e7fe-0be9-4097-b617-e3373fa0535e"
DISK_SIZE = 2 * 1024 * 1024 # (2MB)
Expand All @@ -23,11 +23,13 @@ def create_image(tmp_path):
# this should produce a disk that can be opened and validated with GPT tools
new_disk = disk.Disk(image)
new_disk.create(DISK_SIZE)
part1 = Partition(PART1_NAME, PART1_SIZE, Partition.LINUX_FILE_SYSTEM)
part1 = Partition(PART1_NAME, PART1_SIZE, PartitionType.LINUX_FILE_SYSTEM.value)
part1.attribute_flags = PartitionAttribute.HIDDEN
new_disk.table.partitions.add(part1)

part2 = Partition(PART2_NAME, PART2_SIZE, Partition.LINUX_FILE_SYSTEM, STATIC_UUID)
part2 = Partition(
PART2_NAME, PART2_SIZE, PartitionType.LINUX_FILE_SYSTEM.value, STATIC_UUID
)
new_disk.table.partitions.add(part2)

new_disk.commit()
Expand Down
53 changes: 32 additions & 21 deletions tests/test_partition.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,27 @@
import pytest

from gpt_image.geometry import Geometry
from gpt_image.partition import (
Partition,
PartitionAttribute,
PartitionEntryArray,
PartitionEntryError,
)
from gpt_image.partition import (Partition, PartitionAttribute,
PartitionEntryArray, PartitionEntryError,
PartitionType)

PART_NAME = "test-part"
PART_NAME_2 = "partition-2"
PART_UUID = "26be6d04-85fe-4fae-ba9c-1f47cf16f8d8"


def test_partition_init_guid():
part = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part = Partition(PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
assert isinstance(uuid.UUID(part.partition_guid), uuid.UUID)
del part
part = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM, PART_UUID)
part = Partition(
PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value, PART_UUID
)
assert part.partition_guid == PART_UUID


def test_partition_repr():
part = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part = Partition(PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part.attribute_flags = PartitionAttribute.READ_ONLY
part_s = str(part)
assert PART_NAME in part_s
Expand All @@ -37,7 +36,7 @@ def test_partition_repr():


def test_partition_attribute():
part = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part = Partition(PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
# flags are set to NONE by default
assert part.attribute_flags == []

Expand All @@ -59,11 +58,14 @@ def test_partition_attribute():

def test_partition_marshal():
part = Partition(
PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM, partition_guid=PART_UUID
PART_NAME,
2 * 1024,
PartitionType.LINUX_FILE_SYSTEM.value,
partition_guid=PART_UUID,
)
part.attribute_flags = PartitionAttribute.READ_ONLY
part_bytes = part.marshal()
assert part_bytes[0:16] == uuid.UUID(Partition.LINUX_FILE_SYSTEM).bytes_le
assert part_bytes[0:16] == uuid.UUID(PartitionType.LINUX_FILE_SYSTEM.value).bytes_le
assert part_bytes[16:32] == uuid.UUID(PART_UUID).bytes_le
assert part_bytes[32:40] == b"\x00" * 8
assert part_bytes[40:48] == b"\x00" * 8
Expand All @@ -80,41 +82,50 @@ def test_partition_read():
geo = Geometry(8 * 1024 * 1024)
part_array = PartitionEntryArray(geo)
part = Partition(
PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM, partition_guid=PART_UUID
PART_NAME,
2 * 1024,
PartitionType.LINUX_FILE_SYSTEM.value,
partition_guid=PART_UUID,
)
part_array.add(part)
part_bytes = part.marshal()
new_part = Partition.unmarshal(part_bytes, geo.sector_size)
assert new_part.partition_name == PART_NAME
assert new_part.size == 2 * 1024
assert new_part.type_guid.upper() == Partition.LINUX_FILE_SYSTEM
assert new_part.type_guid.upper() == PartitionType.LINUX_FILE_SYSTEM.value
assert new_part.partition_guid == PART_UUID
# @TODO: read attributes from existing partitions


def test_partition_entry_add():
geo = Geometry(8 * 1024 * 1024)
part_array = PartitionEntryArray(geo)
part1 = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part1 = Partition(PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part_array.add(part1)
# check partition LBAs. The default alignment is 8 sectors so the
# first LBA will always be a factor of 8
assert part1.first_lba == 40
assert part1.last_lba == 43
part2 = Partition(PART_NAME_2, 3 * 1024, Partition.LINUX_FILE_SYSTEM)
part2 = Partition(PART_NAME_2, 3 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part_array.add(part2)
assert part2.first_lba == 48
assert part2.last_lba == 53

assert len(part_array.entries) == 2
part3 = Partition(
"partition3", 6 * 1024, PartitionType.LINUX_FILE_SYSTEM.value, alignment=20
)
part_array.add(part3)
assert part3.first_lba == 60
assert part3.last_lba == 71


def test_partition_entry_add_too_large():
geo = Geometry(8 * 1024 * 1024)
part_array = PartitionEntryArray(geo)
part1 = Partition("part1", 2 * 1024 * 1024, Partition.LINUX_FILE_SYSTEM)
part2 = Partition("part2", 10 * 1024 * 1024, Partition.LINUX_FILE_SYSTEM)
part3 = Partition("part3", 5 * 1024 * 1024, Partition.LINUX_FILE_SYSTEM)
part1 = Partition("part1", 2 * 1024 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part2 = Partition("part2", 10 * 1024 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part3 = Partition("part3", 5 * 1024 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)

# this should fit
part_array.add(part1)
Expand All @@ -128,8 +139,8 @@ def test_partition_entry_add_too_large():
def test_partition_entry_marshall():
geo = Geometry(8 * 1024 * 1024)
part_array = PartitionEntryArray(geo)
part1 = Partition(PART_NAME, 2 * 1024, Partition.LINUX_FILE_SYSTEM)
part2 = Partition(PART_NAME_2, 3 * 1024, Partition.LINUX_FILE_SYSTEM)
part1 = Partition(PART_NAME, 2 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part2 = Partition(PART_NAME_2, 3 * 1024, PartitionType.LINUX_FILE_SYSTEM.value)
part_array.add(part1)
part_array.add(part2)

Expand Down

0 comments on commit e45c102

Please sign in to comment.