|
14 | 14 |
|
15 | 15 | #![deny(unused_results)]
|
16 | 16 | #![deny(missing_docs)]
|
17 |
| -// We're just a wrapper around openat, shouldn't have any unsafe here. |
18 |
| -#![forbid(unsafe_code)] |
| 17 | +#![deny(unsafe_code)] |
19 | 18 |
|
20 | 19 | const TEMPFILE_ATTEMPTS: u32 = 100;
|
21 | 20 |
|
@@ -78,6 +77,9 @@ pub trait OpenatDirExt {
|
78 | 77 | /// Remove all content at the target path, returns `true` if something existed there.
|
79 | 78 | fn remove_all<P: openat::AsPath>(&self, p: P) -> io::Result<bool>;
|
80 | 79 |
|
| 80 | + /// Synchronize to disk the filesystem containing this directory. |
| 81 | + fn syncfs(&self) -> io::Result<()>; |
| 82 | + |
81 | 83 | /// Copy a regular file. The semantics here are intended to match `std::fs::copy()`.
|
82 | 84 | /// If the target exists, it will be overwritten. The mode bits (permissions) will match, but
|
83 | 85 | /// owner/group will be derived from the current process. Extended attributes are not
|
@@ -292,6 +294,20 @@ impl OpenatDirExt for openat::Dir {
|
292 | 294 | impl_remove_all(self, p)
|
293 | 295 | }
|
294 | 296 |
|
| 297 | + #[allow(unsafe_code)] |
| 298 | + fn syncfs(&self) -> io::Result<()> { |
| 299 | + // syncfs(2) does not work with `O_PATH` FDs, so `self` cannot |
| 300 | + // be directly used. Thus we have to materialize a FD for the |
| 301 | + // directory first. |
| 302 | + let dirfd = self.open_file(".")?; |
| 303 | + let ret = unsafe { libc::syncfs(dirfd.as_raw_fd()) }; |
| 304 | + if ret == 0 { |
| 305 | + Ok(()) |
| 306 | + } else { |
| 307 | + Err(std::io::Error::last_os_error()) |
| 308 | + } |
| 309 | + } |
| 310 | + |
295 | 311 | fn new_file_writer<'a>(&'a self, mode: libc::mode_t) -> io::Result<FileWriter> {
|
296 | 312 | let (tmpf, name) = if let Some(tmpf) = self.new_unnamed_file(mode).ok() {
|
297 | 313 | (tmpf, None)
|
@@ -753,6 +769,15 @@ mod tests {
|
753 | 769 | Ok(fallback)
|
754 | 770 | }
|
755 | 771 |
|
| 772 | + #[test] |
| 773 | + fn test_syncfs() { |
| 774 | + let td = tempfile::tempdir().unwrap(); |
| 775 | + let d = openat::Dir::open(td.path()).unwrap(); |
| 776 | + d.ensure_dir_all("foo/bar", 0o755).unwrap(); |
| 777 | + d.syncfs().unwrap(); |
| 778 | + assert_eq!(d.exists("foo/bar").unwrap(), true); |
| 779 | + } |
| 780 | + |
756 | 781 | #[test]
|
757 | 782 | fn copy_fallback() -> Result<()> {
|
758 | 783 | use std::io::Read;
|
|
0 commit comments