Skip to content

Commit 05a3e65

Browse files
committed
partitioning: Namespace blkpg APIs
Signed-off-by: Ikey Doherty <[email protected]>
1 parent 7410250 commit 05a3e65

File tree

3 files changed

+189
-182
lines changed

3 files changed

+189
-182
lines changed

crates/partitioning/src/blkpg.rs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use disks::{BasicDisk, DiskInit};
6+
use log::{debug, error, info, warn};
7+
use std::{
8+
fs::File,
9+
io,
10+
os::fd::{AsFd, AsRawFd},
11+
path::{Path, PathBuf},
12+
};
13+
use thiserror::Error;
14+
15+
pub use gpt;
16+
use linux_raw_sys::ioctl::BLKPG;
17+
use nix::libc;
18+
19+
/// Errors that can occur during partition operations
20+
#[derive(Error, Debug)]
21+
pub enum Error {
22+
/// IO operation error
23+
#[error("IO error: {0}")]
24+
Io(#[from] io::Error),
25+
/// GPT-specific error
26+
#[error("GPT error: {0}")]
27+
Gpt(#[from] gpt::GptError),
28+
}
29+
30+
/// Represents a block device partition for IOCTL operations
31+
#[repr(C)]
32+
struct BlkpgPartition {
33+
start: i64,
34+
length: i64,
35+
pno: i32,
36+
devname: [u8; 64],
37+
volname: [u8; 64],
38+
}
39+
40+
/// IOCTL structure for partition operations
41+
#[repr(C)]
42+
struct BlkpgIoctl {
43+
op: i32,
44+
flags: i32,
45+
datalen: i32,
46+
data: *mut BlkpgPartition,
47+
}
48+
49+
const BLKPG_ADD_PARTITION: i32 = 1;
50+
const BLKPG_DEL_PARTITION: i32 = 2;
51+
52+
/// Adds a new partition to the specified block device
53+
///
54+
/// # Arguments
55+
/// * `fd` - File descriptor for the block device
56+
/// * `partition_number` - Number to assign to the new partition
57+
/// * `start` - Starting offset in bytes
58+
/// * `length` - Length of partition in bytes
59+
///
60+
/// # Returns
61+
/// `io::Result<()>` indicating success or failure
62+
pub(crate) fn add_partition<F>(fd: F, partition_number: i32, start: i64, length: i64) -> io::Result<()>
63+
where
64+
F: AsRawFd,
65+
{
66+
info!(
67+
"➕ Adding partition {} (start: {}, length: {})",
68+
partition_number, start, length
69+
);
70+
let mut part = BlkpgPartition {
71+
start,
72+
length,
73+
pno: partition_number,
74+
devname: [0; 64],
75+
volname: [0; 64],
76+
};
77+
78+
let mut ioctl = BlkpgIoctl {
79+
op: BLKPG_ADD_PARTITION,
80+
flags: 0,
81+
datalen: std::mem::size_of::<BlkpgPartition>() as i32,
82+
data: &mut part,
83+
};
84+
85+
let res = unsafe { libc::ioctl(fd.as_raw_fd(), BLKPG as _, &mut ioctl) };
86+
if res < 0 {
87+
let err = io::Error::last_os_error();
88+
error!("❌ Failed to add partition: {}", err);
89+
return Err(err);
90+
}
91+
info!("✅ Successfully added partition {}", partition_number);
92+
Ok(())
93+
}
94+
95+
/// Deletes a partition from the specified block device
96+
///
97+
/// # Arguments
98+
/// * `fd` - File descriptor for the block device
99+
/// * `partition_number` - Number of the partition to delete
100+
///
101+
/// # Returns
102+
/// `io::Result<()>` indicating success or failure
103+
pub(crate) fn delete_partition<F>(fd: F, partition_number: i32) -> io::Result<()>
104+
where
105+
F: AsRawFd,
106+
{
107+
warn!("🗑️ Attempting to delete partition {}", partition_number);
108+
let mut part = BlkpgPartition {
109+
start: 0,
110+
length: 0,
111+
pno: partition_number,
112+
devname: [0; 64],
113+
volname: [0; 64],
114+
};
115+
116+
let mut ioctl = BlkpgIoctl {
117+
op: BLKPG_DEL_PARTITION,
118+
flags: 0,
119+
datalen: std::mem::size_of::<BlkpgPartition>() as i32,
120+
data: &mut part,
121+
};
122+
123+
let res = unsafe { libc::ioctl(fd.as_raw_fd(), BLKPG as _, &mut ioctl) };
124+
if res < 0 {
125+
let err = io::Error::last_os_error();
126+
error!("❌ Failed to delete partition {}: {}", partition_number, err);
127+
return Err(err);
128+
}
129+
info!("✅ Successfully deleted partition {}", partition_number);
130+
Ok(())
131+
}
132+
133+
/// Updates kernel partition representations to match the GPT table
134+
///
135+
/// # Arguments
136+
/// * `path` - Path to the block device
137+
///
138+
/// # Returns
139+
/// `Result<(), Error>` indicating success or partition operation failure
140+
pub fn sync_gpt_partitions<P: AsRef<Path>>(path: P) -> Result<(), Error> {
141+
info!("🔄 Syncing GPT partitions for {:?}", path.as_ref());
142+
let file = File::open(&path)?;
143+
144+
// Read GPT table
145+
debug!("📖 Reading GPT table...");
146+
let gpt = gpt::GptConfig::new().writable(false).open(&path)?;
147+
let partitions = gpt.partitions();
148+
let block_size = 512;
149+
info!(
150+
"📊 Found {} partitions with block size {}",
151+
partitions.len(),
152+
block_size
153+
);
154+
155+
warn!("🗑️ Deleting existing partitions...");
156+
157+
// Find the disk for enumeration purposes
158+
let base_name = path
159+
.as_ref()
160+
.file_name()
161+
.ok_or(Error::Io(io::Error::from(io::ErrorKind::InvalidInput)))?
162+
.to_string_lossy()
163+
.to_string();
164+
let disk = BasicDisk::from_sysfs_path(&PathBuf::from("/sys/class/block"), &base_name)
165+
.ok_or(Error::Io(io::Error::from(io::ErrorKind::InvalidInput)))?;
166+
167+
for partition in disk.partitions() {
168+
let _ = delete_partition(file.as_raw_fd(), partition.number as i32);
169+
}
170+
171+
// Add partitions from GPT
172+
info!("➕ Adding new partitions from GPT...");
173+
for (i, partition) in partitions.iter() {
174+
add_partition(
175+
file.as_fd(),
176+
*i as i32,
177+
partition.first_lba as i64 * block_size,
178+
(partition.last_lba - partition.first_lba + 1) as i64 * block_size,
179+
)?;
180+
}
181+
182+
info!("✨ GPT partition sync completed successfully");
183+
Ok(())
184+
}

crates/partitioning/src/lib.rs

Lines changed: 1 addition & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -2,187 +2,8 @@
22
//
33
// SPDX-License-Identifier: MPL-2.0
44

5-
/// Provides functionality for managing block device partitions
5+
pub mod blkpg;
66
pub mod loopback;
77
pub mod sparsefile;
88

9-
use disks::{BasicDisk, DiskInit};
10-
use log::{debug, error, info, warn};
11-
use std::{
12-
fs::File,
13-
io,
14-
os::fd::{AsFd, AsRawFd},
15-
path::{Path, PathBuf},
16-
};
17-
use thiserror::Error;
18-
199
pub use gpt;
20-
use linux_raw_sys::ioctl::BLKPG;
21-
use nix::libc;
22-
23-
/// Errors that can occur during partition operations
24-
#[derive(Error, Debug)]
25-
pub enum Error {
26-
/// IO operation error
27-
#[error("IO error: {0}")]
28-
Io(#[from] io::Error),
29-
/// GPT-specific error
30-
#[error("GPT error: {0}")]
31-
Gpt(#[from] gpt::GptError),
32-
}
33-
34-
/// Represents a block device partition for IOCTL operations
35-
#[repr(C)]
36-
struct BlkpgPartition {
37-
start: i64,
38-
length: i64,
39-
pno: i32,
40-
devname: [u8; 64],
41-
volname: [u8; 64],
42-
}
43-
44-
/// IOCTL structure for partition operations
45-
#[repr(C)]
46-
struct BlkpgIoctl {
47-
op: i32,
48-
flags: i32,
49-
datalen: i32,
50-
data: *mut BlkpgPartition,
51-
}
52-
53-
const BLKPG_ADD_PARTITION: i32 = 1;
54-
const BLKPG_DEL_PARTITION: i32 = 2;
55-
56-
/// Adds a new partition to the specified block device
57-
///
58-
/// # Arguments
59-
/// * `fd` - File descriptor for the block device
60-
/// * `partition_number` - Number to assign to the new partition
61-
/// * `start` - Starting offset in bytes
62-
/// * `length` - Length of partition in bytes
63-
///
64-
/// # Returns
65-
/// `io::Result<()>` indicating success or failure
66-
pub(crate) fn add_partition<F>(fd: F, partition_number: i32, start: i64, length: i64) -> io::Result<()>
67-
where
68-
F: AsRawFd,
69-
{
70-
info!(
71-
"➕ Adding partition {} (start: {}, length: {})",
72-
partition_number, start, length
73-
);
74-
let mut part = BlkpgPartition {
75-
start,
76-
length,
77-
pno: partition_number,
78-
devname: [0; 64],
79-
volname: [0; 64],
80-
};
81-
82-
let mut ioctl = BlkpgIoctl {
83-
op: BLKPG_ADD_PARTITION,
84-
flags: 0,
85-
datalen: std::mem::size_of::<BlkpgPartition>() as i32,
86-
data: &mut part,
87-
};
88-
89-
let res = unsafe { libc::ioctl(fd.as_raw_fd(), BLKPG as _, &mut ioctl) };
90-
if res < 0 {
91-
let err = io::Error::last_os_error();
92-
error!("❌ Failed to add partition: {}", err);
93-
return Err(err);
94-
}
95-
info!("✅ Successfully added partition {}", partition_number);
96-
Ok(())
97-
}
98-
99-
/// Deletes a partition from the specified block device
100-
///
101-
/// # Arguments
102-
/// * `fd` - File descriptor for the block device
103-
/// * `partition_number` - Number of the partition to delete
104-
///
105-
/// # Returns
106-
/// `io::Result<()>` indicating success or failure
107-
pub(crate) fn delete_partition<F>(fd: F, partition_number: i32) -> io::Result<()>
108-
where
109-
F: AsRawFd,
110-
{
111-
warn!("🗑️ Attempting to delete partition {}", partition_number);
112-
let mut part = BlkpgPartition {
113-
start: 0,
114-
length: 0,
115-
pno: partition_number,
116-
devname: [0; 64],
117-
volname: [0; 64],
118-
};
119-
120-
let mut ioctl = BlkpgIoctl {
121-
op: BLKPG_DEL_PARTITION,
122-
flags: 0,
123-
datalen: std::mem::size_of::<BlkpgPartition>() as i32,
124-
data: &mut part,
125-
};
126-
127-
let res = unsafe { libc::ioctl(fd.as_raw_fd(), BLKPG as _, &mut ioctl) };
128-
if res < 0 {
129-
let err = io::Error::last_os_error();
130-
error!("❌ Failed to delete partition {}: {}", partition_number, err);
131-
return Err(err);
132-
}
133-
info!("✅ Successfully deleted partition {}", partition_number);
134-
Ok(())
135-
}
136-
137-
/// Updates kernel partition representations to match the GPT table
138-
///
139-
/// # Arguments
140-
/// * `path` - Path to the block device
141-
///
142-
/// # Returns
143-
/// `Result<(), Error>` indicating success or partition operation failure
144-
pub fn sync_gpt_partitions<P: AsRef<Path>>(path: P) -> Result<(), Error> {
145-
info!("🔄 Syncing GPT partitions for {:?}", path.as_ref());
146-
let file = File::open(&path)?;
147-
148-
// Read GPT table
149-
debug!("📖 Reading GPT table...");
150-
let gpt = gpt::GptConfig::new().writable(false).open(&path)?;
151-
let partitions = gpt.partitions();
152-
let block_size = 512;
153-
info!(
154-
"📊 Found {} partitions with block size {}",
155-
partitions.len(),
156-
block_size
157-
);
158-
159-
warn!("🗑️ Deleting existing partitions...");
160-
161-
// Find the disk for enumeration purposes
162-
let base_name = path
163-
.as_ref()
164-
.file_name()
165-
.ok_or(Error::Io(io::Error::from(io::ErrorKind::InvalidInput)))?
166-
.to_string_lossy()
167-
.to_string();
168-
let disk = BasicDisk::from_sysfs_path(&PathBuf::from("/sys/class/block"), &base_name)
169-
.ok_or(Error::Io(io::Error::from(io::ErrorKind::InvalidInput)))?;
170-
171-
for partition in disk.partitions() {
172-
let _ = delete_partition(file.as_raw_fd(), partition.number as i32);
173-
}
174-
175-
// Add partitions from GPT
176-
info!("➕ Adding new partitions from GPT...");
177-
for (i, partition) in partitions.iter() {
178-
add_partition(
179-
file.as_fd(),
180-
*i as i32,
181-
partition.first_lba as i64 * block_size,
182-
(partition.last_lba - partition.first_lba + 1) as i64 * block_size,
183-
)?;
184-
}
185-
186-
info!("✨ GPT partition sync completed successfully");
187-
Ok(())
188-
}

disk-test/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ use std::{fs, path::Path};
88
use disks::BlockDevice;
99
use partitioning::{
1010
gpt::{disk::LogicalBlockSize, mbr::ProtectiveMBR, partition_types, GptConfig},
11-
loopback, sparsefile, sync_gpt_partitions,
11+
loopback, sparsefile,
1212
};
1313

14+
use partitioning::blkpg;
15+
1416
/// Creates a protective MBR on the specified disk
1517
///
1618
/// # Arguments
@@ -109,7 +111,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
109111

110112
// Notify kernel of partition table changes
111113
debug!("🔄 Syncing partition table changes");
112-
sync_gpt_partitions(&device.path)?;
114+
blkpg::sync_gpt_partitions(&device.path)?;
113115

114116
// Get list of all loopback devices
115117
info!("🔍 Discovering block devices");

0 commit comments

Comments
 (0)