import contextlib
import dataclasses
import enum
+import functools
import math
import os
import resource
return ', '.join(filter(None, desc))
+@functools.lru_cache(maxsize=None)
+def sfdisk_grain_is_supported() -> bool:
+ cmd: List[PathString] = ["sfdisk", "--no-reread", "--no-act", "--quiet", "/dev/full"]
+
+ try:
+ run(cmd, text=True, input='\n'.join(["label: gpt", "grain: 4096", "quit"]), stderr=subprocess.DEVNULL)
+ except subprocess.CalledProcessError:
+ return False
+
+ return True
+
+
@dataclasses.dataclass
class PartitionTable:
partitions: Dict[PartitionIdentifier, Partition] = dataclasses.field(default_factory=dict)
grain: int = 4096
+ def __post_init__(self) -> None:
+ if not sfdisk_grain_is_supported():
+ self.grain = 1024 ** 2 # Use sfdisk default grain size of 1 MiB.
+
def first_partition_offset(self, max_partitions: int = 128) -> int:
if self.first_lba is not None:
# No rounding here, we honour the specified value exactly.
def sfdisk_spec(self) -> str:
table = ["label: gpt",
- f"grain: {self.grain}",
+ f"grain: {self.grain}" if sfdisk_grain_is_supported() else '\n',
f"first-lba: {self.first_partition_offset() // self.sector_size}",
*(p.sfdisk_spec() for p in self.partitions.values())]
return '\n'.join(table)