Skip to content

Commit 8ee9366

Browse files
bors[bot]asomers
andauthored
Merge #1640
1640: Add fspacectl on FreeBSD r=rtzoeller a=asomers Co-authored-by: Alan Somers <asomers@gmail.com>
2 parents 0c02ee2 + e2ce5ef commit 8ee9366

File tree

3 files changed

+128
-1
lines changed

3 files changed

+128
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3737
(#[1628](https://github.com/nix-rust/nix/pull/1628))
3838
- Added `MAP_FIXED_NOREPLACE` on Linux.
3939
(#[1636](https://github.com/nix-rust/nix/pull/1636))
40+
- Added `fspacectl` on FreeBSD
41+
(#[1640](https://github.com/nix-rust/nix/pull/1640))
4042

4143
### Changed
4244

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ targets = [
2727
]
2828

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

src/fcntl.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,131 @@ pub fn fallocate(
697697
Errno::result(res).map(drop)
698698
}
699699

700+
/// Argument to [`fspacectl`] describing the range to zero. The first member is
701+
/// the file offset, and the second is the length of the region.
702+
#[cfg(any(target_os = "freebsd"))]
703+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
704+
pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
705+
706+
#[cfg(any(target_os = "freebsd"))]
707+
impl SpacectlRange {
708+
#[inline]
709+
pub fn is_empty(&self) -> bool {
710+
self.1 == 0
711+
}
712+
713+
#[inline]
714+
pub fn len(&self) -> libc::off_t {
715+
self.1
716+
}
717+
718+
#[inline]
719+
pub fn offset(&self) -> libc::off_t {
720+
self.0
721+
}
722+
}
723+
724+
/// Punch holes in a file.
725+
///
726+
/// `fspacectl` instructs the file system to deallocate a portion of a file.
727+
/// After a successful operation, this region of the file will return all zeroes
728+
/// if read. If the file system supports deallocation, then it may free the
729+
/// underlying storage, too.
730+
///
731+
/// # Arguments
732+
///
733+
/// - `fd` - File to operate on
734+
/// - `range.0` - File offset at which to begin deallocation
735+
/// - `range.1` - Length of the region to deallocate
736+
///
737+
/// # Returns
738+
///
739+
/// The operation may deallocate less than the entire requested region. On
740+
/// success, it returns the region that still remains to be deallocated. The
741+
/// caller should loop until the returned region is empty.
742+
///
743+
/// # Example
744+
///
745+
// no_run because it fails to link until FreeBSD 14.0
746+
/// ```no_run
747+
/// # use std::io::Write;
748+
/// # use std::os::unix::fs::FileExt;
749+
/// # use std::os::unix::io::AsRawFd;
750+
/// # use nix::fcntl::*;
751+
/// # use tempfile::tempfile;
752+
/// const INITIAL: &[u8] = b"0123456789abcdef";
753+
/// let mut f = tempfile().unwrap();
754+
/// f.write_all(INITIAL).unwrap();
755+
/// let mut range = SpacectlRange(3, 6);
756+
/// while (!range.is_empty()) {
757+
/// range = fspacectl(f.as_raw_fd(), range).unwrap();
758+
/// }
759+
/// let mut buf = vec![0; INITIAL.len()];
760+
/// f.read_exact_at(&mut buf, 0).unwrap();
761+
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
762+
/// ```
763+
#[cfg(target_os = "freebsd")]
764+
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
765+
let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
766+
let res = unsafe { libc::fspacectl(
767+
fd,
768+
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
769+
&rqsr,
770+
0, // No flags are currently supported
771+
&mut rqsr
772+
)};
773+
Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
774+
}
775+
776+
/// Like [`fspacectl`], but will never return incomplete.
777+
///
778+
/// # Arguments
779+
///
780+
/// - `fd` - File to operate on
781+
/// - `offset` - File offset at which to begin deallocation
782+
/// - `len` - Length of the region to deallocate
783+
///
784+
/// # Returns
785+
///
786+
/// Returns `()` on success. On failure, the region may or may not be partially
787+
/// deallocated.
788+
///
789+
/// # Example
790+
///
791+
// no_run because it fails to link until FreeBSD 14.0
792+
/// ```no_run
793+
/// # use std::io::Write;
794+
/// # use std::os::unix::fs::FileExt;
795+
/// # use std::os::unix::io::AsRawFd;
796+
/// # use nix::fcntl::*;
797+
/// # use tempfile::tempfile;
798+
/// const INITIAL: &[u8] = b"0123456789abcdef";
799+
/// let mut f = tempfile().unwrap();
800+
/// f.write_all(INITIAL).unwrap();
801+
/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
802+
/// let mut buf = vec![0; INITIAL.len()];
803+
/// f.read_exact_at(&mut buf, 0).unwrap();
804+
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
805+
/// ```
806+
#[cfg(target_os = "freebsd")]
807+
pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
808+
-> Result<()>
809+
{
810+
let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
811+
while rqsr.r_len > 0 {
812+
let res = unsafe { libc::fspacectl(
813+
fd,
814+
libc::SPACECTL_DEALLOC, // Only one command is supported ATM
815+
&rqsr,
816+
0, // No flags are currently supported
817+
&mut rqsr
818+
)};
819+
if let Err(e) = Errno::result(res) {
820+
return Err(e);
821+
}
822+
}
823+
Ok(())
824+
}
700825

701826
#[cfg(any(
702827
target_os = "linux",

0 commit comments

Comments
 (0)