Skip to main content

btrfs_uapi/
blkdev.rs

1//! # Block device ioctls: BLKGETSIZE64, BLKDISCARD
2//!
3//! Standard Linux block device ioctls that are not btrfs-specific but are
4//! needed for device preparation (e.g. querying device size, issuing TRIM).
5
6use std::os::{fd::AsRawFd, unix::io::BorrowedFd};
7
8// From linux/fs.h:
9// #define BLKGETSIZE64 _IOR(0x12, 114, size_t)
10// #define BLKDISCARD   _IO(0x12, 119)
11nix::ioctl_read!(
12    /// Raw ioctl wrapper for `BLKGETSIZE64` (`_IOR(0x12, 114)`).
13    blk_getsize64,
14    0x12,
15    114,
16    u64
17);
18nix::ioctl_write_ptr!(
19    /// Raw ioctl wrapper for `BLKDISCARD` (`_IO(0x12, 119)`).
20    blk_discard,
21    0x12,
22    119,
23    [u64; 2]
24);
25
26/// Get the size of a block device in bytes.
27///
28/// # Errors
29///
30/// Returns `Err` if the `BLKGETSIZE64` ioctl fails.
31pub fn device_size(fd: BorrowedFd) -> nix::Result<u64> {
32    let mut size: u64 = 0;
33    unsafe { blk_getsize64(fd.as_raw_fd(), &raw mut size) }?;
34    Ok(size)
35}
36
37/// Issue a BLKDISCARD (TRIM) on the given byte range of a block device.
38///
39/// Tells the device that the specified range is no longer in use and its
40/// contents can be discarded. This is typically done before repurposing a
41/// device (e.g. as a replace target).
42///
43/// # Errors
44///
45/// Returns `Err` if the `BLKDISCARD` ioctl fails.
46pub fn discard_range(
47    fd: BorrowedFd,
48    offset: u64,
49    length: u64,
50) -> nix::Result<()> {
51    let range: [u64; 2] = [offset, length];
52    unsafe { blk_discard(fd.as_raw_fd(), &raw const range) }?;
53    Ok(())
54}
55
56/// Issue a BLKDISCARD on the entire block device.
57///
58/// Returns the number of bytes discarded (the device size), or an error.
59/// Silently ignores `EOPNOTSUPP` (device does not support discard).
60///
61/// # Errors
62///
63/// Returns `Err` if querying the device size or issuing the discard fails
64/// (other than `EOPNOTSUPP`).
65pub fn discard_whole_device(fd: BorrowedFd) -> nix::Result<u64> {
66    let size = device_size(fd)?;
67    if size == 0 {
68        return Ok(0);
69    }
70    match discard_range(fd, 0, size) {
71        Ok(()) => Ok(size),
72        Err(nix::errno::Errno::EOPNOTSUPP) => Ok(0),
73        Err(e) => Err(e),
74    }
75}