Skip to content

Commit da8a623

Browse files
committed
partitioning: Implement BLKPG reload of partition tables with GPT
Signed-off-by: Ikey Doherty <[email protected]>
1 parent 8ce059d commit da8a623

File tree

4 files changed

+205
-2
lines changed

4 files changed

+205
-2
lines changed

crates/partitioning/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,9 @@ edition = "2021"
55
description = "A library for working directly with partitions"
66

77
[dependencies]
8+
disks = { path = "../disks" }
9+
thiserror.workspace = true
10+
log.workspace = true
11+
gpt.workspace = true
812
nix.workspace = true
9-
linux-raw-sys = { workspace = true, features = ["loop_device"] }
13+
linux-raw-sys = { workspace = true, features = ["loop_device", "ioctl"] }

crates/partitioning/src/lib.rs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,187 @@
22
//
33
// SPDX-License-Identifier: MPL-2.0
44

5+
/// Provides functionality for managing block device partitions
56
pub mod loopback;
67
pub mod sparsefile;
8+
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+
19+
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+
}

crates/partitioning/src/loopback.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::{
88
};
99

1010
use linux_raw_sys::loop_device::{LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_FD, LOOP_SET_STATUS64};
11+
use log::{debug, error, info, warn};
1112
use nix::libc;
1213

1314
/// Represents a loop device that can be used to mount files as block devices
@@ -27,17 +28,21 @@ impl LoopDevice {
2728
pub fn create() -> io::Result<Self> {
2829
use std::fs::OpenOptions;
2930

31+
debug!("🔄 Opening loop control device");
3032
let ctrl = OpenOptions::new().read(true).write(true).open("/dev/loop-control")?;
3133

3234
// Get next free loop device number
3335
let devno = unsafe { libc::ioctl(ctrl.as_raw_fd(), LOOP_CTL_GET_FREE as _) };
3436
if devno < 0 {
37+
error!("❌ Failed to get free loop device number");
3538
return Err(io::Error::last_os_error());
3639
}
3740

3841
let path = format!("/dev/loop{}", devno);
42+
info!("🔧 Creating new loop device at {}", path);
3943
let fd = OpenOptions::new().read(true).write(true).open(&path)?.into();
4044

45+
info!("✅ Successfully created loop device {}", path);
4146
Ok(LoopDevice { fd, path })
4247
}
4348

@@ -50,23 +55,27 @@ impl LoopDevice {
5055
/// # Returns
5156
/// `io::Result<()>` indicating success or failure
5257
pub fn attach(&self, backing_file: &str) -> io::Result<()> {
58+
debug!("📎 Attaching backing file {} to {}", backing_file, self.path);
5359
let f = fs::OpenOptions::new().read(true).write(true).open(backing_file)?;
5460

5561
let file_fd = f.as_raw_fd();
5662
let our_fd = self.fd.as_raw_fd();
5763
let res = unsafe { libc::ioctl(our_fd, LOOP_SET_FD as _, file_fd) };
5864

5965
if res < 0 {
66+
error!("❌ Failed to attach backing file {}", backing_file);
6067
return Err(io::Error::last_os_error());
6168
}
6269

6370
// Force loop device to immediately update by setting empty status
6471
let info: linux_raw_sys::loop_device::loop_info64 = unsafe { std::mem::zeroed() };
6572
let res = unsafe { libc::ioctl(our_fd, LOOP_SET_STATUS64 as _, &info) };
6673
if res < 0 {
74+
warn!("⚠️ Failed to update loop device status");
6775
return Err(io::Error::last_os_error());
6876
}
6977

78+
info!("✅ Successfully attached backing file {}", backing_file);
7079
Ok(())
7180
}
7281

@@ -75,11 +84,14 @@ impl LoopDevice {
7584
/// # Returns
7685
/// `io::Result<()>` indicating success or failure
7786
pub fn detach(&self) -> io::Result<()> {
87+
debug!("🔓 Detaching backing file from {}", self.path);
7888
let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), LOOP_CLR_FD as _, 0) };
7989
if res < 0 {
90+
error!("❌ Failed to detach backing file from {}", self.path);
8091
return Err(io::Error::last_os_error());
8192
}
8293

94+
info!("✅ Successfully detached backing file from {}", self.path);
8395
Ok(())
8496
}
8597
}

crates/partitioning/src/sparsefile.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//
33
// SPDX-License-Identifier: MPL-2.0
44

5+
use log::info;
56
use std::{fs, io, path::Path};
67

78
/// Creates a sparse file at the specified path with the given size.
@@ -16,13 +17,17 @@ pub fn create<P>(path: P, size: u64) -> io::Result<()>
1617
where
1718
P: AsRef<Path>,
1819
{
20+
info!("🗂️ Creating sparse file at {:?}", path.as_ref());
21+
1922
let file = fs::OpenOptions::new()
2023
.write(true)
2124
.create(true)
2225
.truncate(true)
23-
.open(path)?;
26+
.open(&path)?;
2427

28+
info!("📝 Setting file size to {} bytes", size);
2529
file.set_len(size)?;
2630

31+
info!("✅ Successfully created sparse file");
2732
Ok(())
2833
}

0 commit comments

Comments
 (0)