@@ -697,6 +697,139 @@ pub fn fallocate(
697
697
Errno :: result( res) . map( drop)
698
698
}
699
699
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
+ }
700
833
701
834
#[ cfg( any(
702
835
target_os = "linux" ,
0 commit comments