shell_rs/
sync.rs

1// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by General Public License that can be found
3// in the LICENSE file.
4
5use std::path::Path;
6
7use crate::error::Error;
8
9#[derive(Debug, PartialEq)]
10pub enum SyncMode {
11    /// Sync all filesystem caches in system to disk.
12    Sync,
13
14    /// Sync the file systems that contain the file.
15    Filesystem,
16
17    /// Sync file data and metadata.
18    File,
19
20    /// Sync only file data, no unneeded metadata.
21    Data,
22}
23
24/// Sychronize cached writes to persistent storage.
25pub fn sync<P: AsRef<Path>>(file: Option<P>, mode: SyncMode) -> Result<(), Error> {
26    if mode == SyncMode::Sync || file.is_none() {
27        return unsafe { nc::sync().map_err(Into::into) };
28    }
29    let file = file.unwrap();
30    let file = file.as_ref();
31    let mut open_flags = nc::O_RDONLY | nc::O_NONBLOCK;
32    let mut fd = unsafe { nc::open(file, open_flags, 0) };
33    if fd.is_err() {
34        open_flags = nc::O_WRONLY | nc::O_NONBLOCK;
35        fd = unsafe { nc::open(file, open_flags, 0) };
36    }
37    let fd = fd?;
38    // We used O_NONBLOCK above to not hang with fifos, so reset that here.
39    let fdflags = unsafe { nc::fcntl(fd, nc::F_GETFL, 0)? };
40    let _ = unsafe { nc::fcntl(fd, nc::F_SETFL, (fdflags & !nc::O_NONBLOCK) as usize)? };
41
42    match mode {
43        SyncMode::Filesystem => unsafe { nc::syncfs(fd)? },
44        SyncMode::File => unsafe { nc::fsync(fd)? },
45        SyncMode::Data => unsafe { nc::fdatasync(fd)? },
46        _ => (),
47    }
48
49    unsafe { nc::close(fd)? };
50    Ok(())
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_sync() {
59        assert!(sync::<&str>(None, SyncMode::Sync).is_ok());
60        assert!(sync(Some("/etc/passwd"), SyncMode::Filesystem).is_ok());
61        assert!(sync(Some("/etc/passwd"), SyncMode::File).is_ok());
62        assert!(sync(Some("/etc/passwd"), SyncMode::Data).is_ok());
63    }
64}