diff --git a/README.adoc b/README.adoc index f78e677..c3acbab 100644 --- a/README.adoc +++ b/README.adoc @@ -24,8 +24,8 @@ Efficiently removing data, and creating read snapshots requires hole punching, a == Supported systems -macOS, Linux and Windows are supported. BSD should work. Solaris requires a small amount of work to complete the implementation. On systems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking. +macOS, Linux and Windows are supported. FreeBSD doesn't work yet, unfortunately file cloning and open file description locking is missing, but there are fallbacks that can be used. Solaris requires a small amount of work to complete the implementation. On systems where block cloning is not supported (ext4 on Linux, and NTFS on Windows are notable examples), the implementation falls back to file region locking (except for FreeBSD which will have to fall back to whole-file locking). == anacrolix/squirrel -I previously wrote https://github.com/anacrolix/squirrel[anacrolix/squirrel], which can fit the role of both in-memory or disk caching due to SQLite's VFS. However, it's written in Go and so is less accessible from other languages. Using SQLite for value storage means large streaming writes are exclusive, even within a single transaction. Value data is serialized to the blob format, potentially across multiple linked pages, and copied multiple times as items are evicted and moved around. Direct file I/O isn't available with SQLite, and the size of values must be known before they can be written, which can mean copying streams to temporary files before they can be written into the SQLite file. \ No newline at end of file +I previously wrote https://github.com/anacrolix/squirrel[anacrolix/squirrel], which can fit the role of both in-memory or disk caching due to SQLite's VFS. However, it's written in Go and so is less accessible from other languages. Using SQLite for value storage means large streaming writes are exclusive, even within a single transaction. Value data is serialized to the blob format, potentially across multiple linked pages, and copied multiple times as items are evicted and moved around. Direct file I/O isn't available with SQLite, and the size of values must be known before they can be written, which can mean copying streams to temporary files before they can be written into the SQLite file. diff --git a/src/sys/clonefile/sys.rs b/src/sys/clonefile/sys.rs index 0e58d46..d6cd1a8 100644 --- a/src/sys/clonefile/sys.rs +++ b/src/sys/clonefile/sys.rs @@ -91,6 +91,16 @@ pub fn fclonefile_noflags(src_file: &File, dst_path: &Path) -> PubResult<()> { if rv == -1 { return Err(last_errno()); } + } else if #[cfg(target_os = "freebsd")] { + // Looks like the syscall isn't ready yet. + // https://github.com/openzfs/zfs/pull/13392#issue-1221750354 + let dst_file = File::create(dst_path)?; + let src_fd = src_file.as_raw_fd(); + let dst_fd = dst_file.as_raw_fd(); + let rv = libc::fclonefile(src_fd, dst_fd); + if rv == -1 { + return Err(last_errno()); + } } else { // assert!(dst_path.is_absolute()); let dst_buf = CPathBuf::try_from(dst_path).unwrap(); diff --git a/src/sys/punchfile/freebsd.rs b/src/sys/punchfile/freebsd.rs new file mode 100644 index 0000000..2eb227b --- /dev/null +++ b/src/sys/punchfile/freebsd.rs @@ -0,0 +1,35 @@ +use std::{io, mem}; +use std::io::Error; + +use super::*; + +pub fn punchfile(file: &File, offset: u64, length: u64) -> io::Result<()> { + // TODO: On solaris we want fcntl(F_FREESP); + let rqsr = libc::spacectl_range { + r_offset: offset.try_into().unwrap(), + r_len: length.try_into().unwrap(), + }; + let mut rmsr: libc::spacectl_range = unsafe { mem::zeroed() }; + let rv = unsafe { + libc::fspacectl( + file.as_fd().as_raw_fd(), + libc::SPACECTL_DEALLOC, + &rqsr, + 0, + &mut rmsr, + ) + }; + if rv == -1 { + return Err(Error::last_os_error()); + } + if rmsr.r_len != 0 { + warn!( + req_off = rqsr.r_offset, + req_len = rqsr.r_len, + unproc_off = rmsr.r_offset, + unproc_len = rmsr.r_len, + "spacectl dealloc returned unprocessed part" + ); + } + Ok(()) +} diff --git a/src/sys/punchfile/mod.rs b/src/sys/punchfile/mod.rs index 619a3b3..9727099 100644 --- a/src/sys/punchfile/mod.rs +++ b/src/sys/punchfile/mod.rs @@ -7,7 +7,11 @@ cfg_if! { } else if #[cfg(windows)] { mod windows; pub use self::windows::*; + } else if #[cfg(target_os = "freebsd")] { + mod freebsd; + pub use freebsd::*; } else { + // Looks like FreeBSD has fspacectl() mod macos; pub use macos::*; }