Skip to content

Commit e91cb35

Browse files
committed
partitioning: Respect boundaries of disks for GPT LBA offsets
Signed-off-by: Ikey Doherty <[email protected]>
1 parent d2529bb commit e91cb35

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

crates/partitioning/src/planner.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ pub enum Change {
4646

4747
/// A disk partitioning planner.
4848
pub struct Planner {
49+
/// First usable LBA position on disk in bytes
50+
usable_start: u64,
51+
/// Last usable LBA position on disk in bytes
52+
usable_end: u64,
4953
/// The original block device state that we're planning changes for
5054
device: BlockDevice,
5155
/// Stack of changes that can be undone
@@ -192,30 +196,49 @@ impl Planner {
192196
/// Creates a new partitioning planner for the given disk.
193197
pub fn new(device: BlockDevice) -> Self {
194198
debug!("Creating new partition planner for device of size {}", device.size());
199+
195200
// Extract original regions from device
196201
let original_regions = device
197202
.partitions()
198203
.iter()
199204
.map(|p| Region::new(p.start, p.end))
200205
.collect();
206+
201207
Self {
208+
usable_start: 0,
209+
usable_end: device.size(),
202210
device,
203211
changes: VecDeque::new(),
204212
original_regions,
205213
}
206214
}
207215

216+
/// Set the usable disk region offsets
217+
pub fn with_start_offset(self, offset: u64) -> Self {
218+
Self {
219+
usable_start: offset,
220+
..self
221+
}
222+
}
223+
224+
/// Set the usable disk region offsets
225+
pub fn with_end_offset(self, offset: u64) -> Self {
226+
Self {
227+
usable_end: offset,
228+
..self
229+
}
230+
}
231+
208232
/// Get a human readable description of pending changes
209233
pub fn describe_changes(&self) -> String {
210234
if self.changes.is_empty() {
211235
return "No pending changes".to_string();
212236
}
213237

214-
let disk_size = self.device.size();
215238
let mut description = "Pending changes:\n".to_string();
216239

217240
for (i, change) in self.changes.iter().enumerate() {
218-
description.push_str(&format!(" {}: {}\n", i + 1, change.describe(disk_size)));
241+
description.push_str(&format!(" {}: {}\n", i + 1, change.describe(self.usable_size())));
219242
}
220243

221244
description
@@ -272,9 +295,9 @@ impl Planner {
272295
let aligned_end = align_down(end, PARTITION_ALIGNMENT);
273296
debug!("Aligned positions: {}..{}", aligned_start, aligned_end);
274297

275-
// Validate bounds
276-
if aligned_end > self.device.size() {
277-
warn!("Partition would exceed disk bounds");
298+
// Validate bounds against usable disk region
299+
if aligned_start < self.usable_start || aligned_end > self.usable_end {
300+
warn!("Partition would be outside usable disk region");
278301
return Err(PlanError::RegionOutOfBounds {
279302
start: aligned_start,
280303
end: aligned_end,
@@ -321,8 +344,8 @@ impl Planner {
321344
if index >= self.original_regions.len() {
322345
warn!("Invalid partition index {}", index);
323346
return Err(PlanError::RegionOutOfBounds {
324-
start: 0,
325-
end: self.device.size(),
347+
start: self.usable_start,
348+
end: self.usable_size(),
326349
});
327350
}
328351

@@ -362,6 +385,17 @@ impl Planner {
362385
pub fn original_device(&self) -> &BlockDevice {
363386
&self.device
364387
}
388+
389+
/// Get the size of the usable disk region in bytes
390+
pub fn usable_size(&self) -> u64 {
391+
self.usable_end - self.usable_start
392+
}
393+
394+
/// Get the usable disk region offsets
395+
pub fn offsets(&self) -> (u64, u64) {
396+
(self.usable_start, self.usable_end)
397+
}
398+
365399
/// Plan to initialize a clean partition layout
366400
pub fn plan_initialize_disk(&mut self) -> Result<(), PlanError> {
367401
debug!("Planning to create new GPT partition table");

crates/partitioning/src/strategy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,8 @@ impl Strategy {
8585

8686
/// Find available free regions on the disk
8787
fn find_free_regions(&self, planner: &Planner) -> Vec<Region> {
88-
let disk_size = planner.original_device().size();
8988
let mut regions = Vec::new();
90-
let mut current = 0;
89+
let (mut current, disk_size) = planner.offsets();
9190

9291
// Sort existing partitions by start position
9392
let mut layout = planner.current_layout();
@@ -147,7 +146,8 @@ impl Strategy {
147146
AllocationStrategy::InitializeWholeDisk => {
148147
// Clear existing partitions and start fresh
149148
planner.plan_initialize_disk()?;
150-
Region::new(0, planner.original_device().size())
149+
let (start, end) = planner.offsets();
150+
Region::new(start, end)
151151
}
152152
AllocationStrategy::LargestFree => {
153153
let free_regions = self.find_free_regions(planner);

disk-test/src/main.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use disks::BlockDevice;
99
use partitioning::{
1010
gpt::{disk::LogicalBlockSize, mbr::ProtectiveMBR, partition_types, GptConfig},
1111
loopback,
12-
planner::Planner,
12+
planner::{format_size, Planner},
1313
sparsefile,
1414
strategy::{AllocationStrategy, PartitionRequest, SizeRequirement, Strategy},
1515
};
@@ -68,10 +68,17 @@ where
6868
let mut gpt_disk = gpt_config.create(&path)?;
6969
gpt_disk.write_inplace()?;
7070

71+
eprintln!("GPT: {:?}", gpt_disk);
72+
73+
let first_usable = gpt_disk.header().first_usable * 512;
74+
let last_usable = gpt_disk.header().last_usable * 512;
75+
7176
// Connect the planner.
7277
let disk = disks::loopback::Device::from_device_path(path).unwrap();
7378
let block = BlockDevice::loopback_device(disk);
74-
let mut planner = Planner::new(block);
79+
let mut planner = Planner::new(block)
80+
.with_start_offset(first_usable)
81+
.with_end_offset(last_usable);
7582
let mut strategy = Strategy::new(AllocationStrategy::InitializeWholeDisk);
7683

7784
// efi
@@ -111,9 +118,19 @@ where
111118
info!("Computed: {}", planner.describe_changes());
112119

113120
// TODO: Track the types in the API and use them here
114-
for partition in planner.current_layout() {
121+
for (n, partition) in planner.current_layout().iter().enumerate() {
115122
info!("Adding partition: {:?}", &partition);
116-
gpt_disk.add_partition("", partition.size(), partition_types::LINUX_FS, 0, None)?;
123+
assert_ne!(0, partition.size());
124+
let size = partitioning::planner::format_size(partition.size());
125+
info!("Partition {} size: {}, at {}", n, size, format_size(partition.start));
126+
gpt_disk.add_partition_at(
127+
"",
128+
n as u32 + 1,
129+
partition.start / 512,
130+
partition.size() / 512,
131+
partition_types::LINUX_FS,
132+
0,
133+
)?;
117134
}
118135

119136
let _ = gpt_disk.write()?;

0 commit comments

Comments
 (0)