Skip to content

Commit 3627f0a

Browse files
committed
partitioning: Start new crate for partitioning APIs
Right now we've simply got helpers to generate sparse files and a new unsafe-bound API for managing loopback devices, so that we can do safer testing for filesystem bits. Signed-off-by: Ikey Doherty <[email protected]>
1 parent 3dfa12f commit 3627f0a

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed

crates/partitioning/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "partitioning"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "A library for working directly with partitions"
6+
7+
[dependencies]
8+
nix.workspace = true
9+
linux-raw-sys = { workspace = true, features = ["loop_device"] }

crates/partitioning/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
pub mod loopback;
6+
pub mod sparsefile;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use std::{
6+
fs, io,
7+
os::fd::{AsRawFd, OwnedFd},
8+
};
9+
10+
use linux_raw_sys::loop_device::{LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_FD, LOOP_SET_STATUS64};
11+
use nix::libc;
12+
13+
/// Represents a loop device that can be used to mount files as block devices
14+
pub struct LoopDevice {
15+
/// File descriptor for the loop device
16+
fd: OwnedFd,
17+
/// Path to the loop device (e.g. /dev/loop0)
18+
pub path: String,
19+
}
20+
21+
impl LoopDevice {
22+
/// Creates a new loop device by obtaining the next available device number
23+
/// from /dev/loop-control and opening the corresponding device file.
24+
///
25+
/// # Returns
26+
/// `io::Result<LoopDevice>` containing the new loop device on success
27+
pub fn create() -> io::Result<Self> {
28+
use std::fs::OpenOptions;
29+
30+
let ctrl = OpenOptions::new().read(true).write(true).open("/dev/loop-control")?;
31+
32+
// Get next free loop device number
33+
let devno = unsafe { libc::ioctl(ctrl.as_raw_fd(), LOOP_CTL_GET_FREE as _) };
34+
if devno < 0 {
35+
return Err(io::Error::last_os_error());
36+
}
37+
38+
let path = format!("/dev/loop{}", devno);
39+
let fd = OpenOptions::new().read(true).write(true).open(&path)?.into();
40+
41+
Ok(LoopDevice { fd, path })
42+
}
43+
44+
/// Attaches a backing file to this loop device, allowing the file to be
45+
/// accessed as a block device.
46+
///
47+
/// # Arguments
48+
/// * `backing_file` - Path to the file to attach
49+
///
50+
/// # Returns
51+
/// `io::Result<()>` indicating success or failure
52+
pub fn attach(&self, backing_file: &str) -> io::Result<()> {
53+
let f = fs::OpenOptions::new().read(true).write(true).open(backing_file)?;
54+
55+
let file_fd = f.as_raw_fd();
56+
let our_fd = self.fd.as_raw_fd();
57+
let res = unsafe { libc::ioctl(our_fd, LOOP_SET_FD as _, file_fd) };
58+
59+
if res < 0 {
60+
return Err(io::Error::last_os_error());
61+
}
62+
63+
// Force loop device to immediately update by setting empty status
64+
let info: linux_raw_sys::loop_device::loop_info64 = unsafe { std::mem::zeroed() };
65+
let res = unsafe { libc::ioctl(our_fd, LOOP_SET_STATUS64 as _, &info) };
66+
if res < 0 {
67+
return Err(io::Error::last_os_error());
68+
}
69+
70+
Ok(())
71+
}
72+
73+
/// Detaches the current backing file from this loop device.
74+
///
75+
/// # Returns
76+
/// `io::Result<()>` indicating success or failure
77+
pub fn detach(&self) -> io::Result<()> {
78+
let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), LOOP_CLR_FD as _, 0) };
79+
if res < 0 {
80+
return Err(io::Error::last_os_error());
81+
}
82+
83+
Ok(())
84+
}
85+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-FileCopyrightText: Copyright © 2025 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
use std::{fs, io, path::Path};
6+
7+
/// Creates a sparse file at the specified path with the given size.
8+
///
9+
/// # Arguments
10+
/// * `path` - Path where the sparse file should be created
11+
/// * `size` - Size in bytes for the sparse file
12+
///
13+
/// # Returns
14+
/// `io::Result<()>` indicating success or failure
15+
pub fn create<P>(path: P, size: u64) -> io::Result<()>
16+
where
17+
P: AsRef<Path>,
18+
{
19+
let file = fs::OpenOptions::new()
20+
.write(true)
21+
.create(true)
22+
.truncate(true)
23+
.open(path)?;
24+
25+
file.set_len(size)?;
26+
27+
Ok(())
28+
}

0 commit comments

Comments
 (0)