@@ -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