Skip to main content

nuclei/fs/
sync_dir.rs

1//! Writing a directory's entries through to the device.
2
3use std::io;
4use std::path::Path;
5
6use super::ops::blocking;
7
8/// Writes the entries of the directory at `path` through to the device: the
9/// names created, removed or renamed in it before the call survive a crash.
10/// A file's own `sync_all` does not cover its name in the directory, so the
11/// name needs this call. It runs on the blocking pool.
12///
13/// To replace a file `target` atomically, so a crash leaves either the old
14/// contents or the new and never a mix:
15///
16/// 1. write the new contents to a temporary file in the same directory,
17/// 2. [`sync_all`](super::File::sync_all) the temporary file,
18/// 3. [`rename`](super::rename) it over `target`,
19/// 4. `sync_dir` the directory they are in.
20///
21/// On Windows the directory is opened with backup semantics for reading and
22/// writing and flushed (`FlushFileBuffers`); on Apple's systems a full flush
23/// of a directory the system refuses is made with `fsync`.
24///
25/// # Errors
26///
27/// The system's own: `path` does not exist, cannot be opened, or is not
28/// something the system lets be synced. A path that is not a directory gets
29/// the answer the system gives for it.
30pub async fn sync_dir(path: impl AsRef<Path>) -> io::Result<()> {
31    let path = path.as_ref().to_path_buf();
32    blocking(move || sync_dir_now(&path)).await
33}
34
35#[cfg(unix)]
36fn sync_dir_now(path: &Path) -> io::Result<()> {
37    let dir = std::fs::File::open(path)?;
38    let synced = dir.sync_all();
39    #[cfg(any(target_os = "macos", target_os = "ios"))]
40    if let Err(err) = &synced
41        && matches!(err.raw_os_error(), Some(libc::ENOTSUP | libc::EINVAL))
42    {
43        use std::os::fd::AsRawFd;
44        // SAFETY: a plain call on the descriptor `dir` keeps open.
45        return match unsafe { libc::fsync(dir.as_raw_fd()) } {
46            0 => Ok(()),
47            _ => Err(io::Error::last_os_error()),
48        };
49    }
50    synced
51}
52
53#[cfg(windows)]
54fn sync_dir_now(path: &Path) -> io::Result<()> {
55    use std::os::windows::fs::OpenOptionsExt;
56    std::fs::OpenOptions::new()
57        .read(true)
58        .write(true)
59        .custom_flags(crate::syscore::windows::FILE_FLAG_BACKUP_SEMANTICS)
60        .open(path)?
61        .sync_all()
62}