Skip to content

Commit

Permalink
Add fspacectl on FreeBSD
Browse files Browse the repository at this point in the history
  • Loading branch information
asomers committed Jan 23, 2022
1 parent c84316b commit ac631d4
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1628](https://github.com/nix-rust/nix/pull/1628))
- Added `MAP_FIXED_NOREPLACE` on Linux.
(#[1636](https://github.com/nix-rust/nix/pull/1636))
- Added `fspacectl` on FreeBSD
(#[1640](https://github.com/nix-rust/nix/pull/1640))

### Changed
### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ targets = [
]

[dependencies]
libc = { git = "https://github.com/rust-lang/libc", rev = "e470e3b6a1f940e0024d40d3b79fc73fe29c7f17", features = [ "extra_traits" ] }
libc = { git = "https://github.com/rust-lang/libc", rev = "7600416f1ca896b501d58b0f44f1869d566359d6", features = [ "extra_traits" ] }
bitflags = "1.1"
cfg-if = "1.0"

Expand Down
133 changes: 133 additions & 0 deletions src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,139 @@ pub fn fallocate(
Errno::result(res).map(drop)
}

/// Argument to [`fspacectl`] describing the range to zero. The first member is
/// the file offset, and the second is the length of the region.
#[cfg(any(target_os = "freebsd"))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);

#[cfg(any(target_os = "freebsd"))]
impl SpacectlRange {
#[inline]
pub fn is_empty(&self) -> bool {
self.1 == 0
}

#[inline]
pub fn len(&self) -> libc::off_t {
self.1
}

#[inline]
pub fn offset(&self) -> libc::off_t {
self.0
}
}

/// Punch holes in a file.
///
/// `fspacectl` instructs the file system to deallocate a portion of a file.
/// After a successful operation, this region of the file will return all zeroes
/// if read. If the file system supports deallocation, then it may free the
/// underlying storage, too.
///
/// # Arguments
///
/// - `fd` - File to operate on
/// - `range.0` - File offset at which to begin deallocation
/// - `range.1` - Length of the region to deallocate
///
/// # Returns
///
/// The operation may deallocate less than the entire requested region. On
/// success, it returns the region that still remains to be deallocated. The
/// caller should loop until the returned region is empty.
///
/// # Example
///
/// ```
/// # use std::io::Write;
/// # use std::os::unix::fs::FileExt;
/// # use std::os::unix::io::AsRawFd;
/// # use nix::fcntl::*;
/// # use tempfile::tempfile;
/// const INITIAL: &[u8] = b"0123456789abcdef";
/// let mut f = tempfile().unwrap();
/// f.write_all(INITIAL).unwrap();
/// let mut range = SpacectlRange(3, 6);
/// while (!range.is_empty()) {
/// let r = fspacectl(f.as_raw_fd(), range);
/// # if r == Err(nix::Error::ENOSYS) {
/// # // not supported until FreeBSD 14.0
/// # return;
/// # }
/// range = r.unwrap();
/// }
/// let mut buf = vec![0; INITIAL.len()];
/// f.read_exact_at(&mut buf, 0).unwrap();
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
/// ```
#[cfg(target_os = "freebsd")]
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
let res = unsafe { libc::fspacectl(
fd,
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr,
0, // No flags are currently supported
&mut rqsr
)};
Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
}

/// Like [`fspacectl`], but will never return incomplete.
///
/// # Arguments
///
/// - `fd` - File to operate on
/// - `offset` - File offset at which to begin deallocation
/// - `len` - Length of the region to deallocate
///
/// # Returns
///
/// Returns `()` on success. On failure, the region may or may not be partially
/// deallocated.
///
/// # Example
///
/// ```
/// # use std::io::Write;
/// # use std::os::unix::fs::FileExt;
/// # use std::os::unix::io::AsRawFd;
/// # use nix::fcntl::*;
/// # use tempfile::tempfile;
/// const INITIAL: &[u8] = b"0123456789abcdef";
/// let mut f = tempfile().unwrap();
/// f.write_all(INITIAL).unwrap();
/// let r = fspacectl_all(f.as_raw_fd(), 3, 6);
/// # if r == Err(nix::Error::ENOSYS) {
/// # // not supported until FreeBSD 14.0
/// # return;
/// # }
/// r.unwrap();
/// let mut buf = vec![0; INITIAL.len()];
/// f.read_exact_at(&mut buf, 0).unwrap();
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
/// ```
#[cfg(target_os = "freebsd")]
pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
-> Result<()>
{
let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
while rqsr.r_len > 0 {
let res = unsafe { libc::fspacectl(
fd,
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
&rqsr,
0, // No flags are currently supported
&mut rqsr
)};
if let Err(e) = Errno::result(res) {
return Err(e);
}
}
Ok(())
}

#[cfg(any(
target_os = "linux",
Expand Down

0 comments on commit ac631d4

Please sign in to comment.