@@ -697,6 +697,139 @@ 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+ /// ```
746+ /// # use std::io::Write;
747+ /// # use std::os::unix::fs::FileExt;
748+ /// # use std::os::unix::io::AsRawFd;
749+ /// # use nix::fcntl::*;
750+ /// # use tempfile::tempfile;
751+ /// const INITIAL: &[u8] = b"0123456789abcdef";
752+ /// let mut f = tempfile().unwrap();
753+ /// f.write_all(INITIAL).unwrap();
754+ /// let mut range = SpacectlRange(3, 6);
755+ /// while (!range.is_empty()) {
756+ /// let r = fspacectl(f.as_raw_fd(), range);
757+ /// # if r == Err(nix::Error::ENOSYS) {
758+ /// # // not supported until FreeBSD 14.0
759+ /// # return;
760+ /// # }
761+ /// range = r.unwrap();
762+ /// }
763+ /// let mut buf = vec![0; INITIAL.len()];
764+ /// f.read_exact_at(&mut buf, 0).unwrap();
765+ /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
766+ /// ```
767+ #[ cfg( target_os = "freebsd" ) ]
768+ pub fn fspacectl( fd: RawFd , range: SpacectlRange ) -> Result <SpacectlRange > {
769+ let mut rqsr = libc:: spacectl_range{ r_offset: range. 0 , r_len: range. 1 } ;
770+ let res = unsafe { libc:: fspacectl(
771+ fd,
772+ libc:: SPACECTL_DEALLOC , // Only one command is supported ATM
773+ & rqsr,
774+ 0 , // No flags are currently supported
775+ & mut rqsr
776+ ) } ;
777+ Errno :: result( res) . map( |_| SpacectlRange ( rqsr. r_offset, rqsr. r_len) )
778+ }
779+
780+ /// Like [`fspacectl`], but will never return incomplete.
781+ ///
782+ /// # Arguments
783+ ///
784+ /// - `fd` - File to operate on
785+ /// - `offset` - File offset at which to begin deallocation
786+ /// - `len` - Length of the region to deallocate
787+ ///
788+ /// # Returns
789+ ///
790+ /// Returns `()` on success. On failure, the region may or may not be partially
791+ /// deallocated.
792+ ///
793+ /// # Example
794+ ///
795+ /// ```
796+ /// # use std::io::Write;
797+ /// # use std::os::unix::fs::FileExt;
798+ /// # use std::os::unix::io::AsRawFd;
799+ /// # use nix::fcntl::*;
800+ /// # use tempfile::tempfile;
801+ /// const INITIAL: &[u8] = b"0123456789abcdef";
802+ /// let mut f = tempfile().unwrap();
803+ /// f.write_all(INITIAL).unwrap();
804+ /// let r = fspacectl_all(f.as_raw_fd(), 3, 6);
805+ /// # if r == Err(nix::Error::ENOSYS) {
806+ /// # // not supported until FreeBSD 14.0
807+ /// # return;
808+ /// # }
809+ /// r.unwrap();
810+ /// let mut buf = vec![0; INITIAL.len()];
811+ /// f.read_exact_at(&mut buf, 0).unwrap();
812+ /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
813+ /// ```
814+ #[ cfg( target_os = "freebsd" ) ]
815+ pub fn fspacectl_all( fd: RawFd , offset: libc:: off_t, len: libc:: off_t)
816+ -> Result <( ) >
817+ {
818+ let mut rqsr = libc:: spacectl_range{ r_offset: offset, r_len: len} ;
819+ while rqsr. r_len > 0 {
820+ let res = unsafe { libc:: fspacectl(
821+ fd,
822+ libc:: SPACECTL_DEALLOC , // Only one command is supported ATM
823+ & rqsr,
824+ 0 , // No flags are currently supported
825+ & mut rqsr
826+ ) } ;
827+ if let Err ( e) = Errno :: result( res) {
828+ return Err ( e) ;
829+ }
830+ }
831+ Ok ( ( ) )
832+ }
700833
701834#[ cfg( any(
702835 target_os = "linux" ,
0 commit comments