diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 36404c4..08b25d7 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -33,7 +33,7 @@ mod rm; mod rmdir; pub mod shitbox; pub mod sleep; -mod sync; +pub mod sync; pub mod r#true; pub mod which; pub mod whoami; @@ -43,8 +43,8 @@ pub use { self::hostname::Hostname, base32::Base32, base64::Base64, basename::Basename, bootstrap::Bootstrap, clear::Clear, dirname::Dirname, echo::Echo, factor::Factor, fold::Fold, groups::Groups, head::Head, link::Link, mountpoint::Mountpoint, nologin::Nologin, nproc::Nproc, - r#false::False, r#true::True, rev::Rev, shitbox::Shitbox, sleep::Sleep, which::Which, - whoami::Whoami, yes::Yes, + r#false::False, r#true::True, rev::Rev, shitbox::Shitbox, sleep::Sleep, sync::SYnc, + which::Which, whoami::Whoami, yes::Yes, }; pub trait Cmd: fmt::Debug + Sync { @@ -75,6 +75,7 @@ pub fn get(name: &str) -> Option> { "rev" => Some(Box::new(Rev::default())), "shitbox" => Some(Box::new(Shitbox::default())), "sleep" => Some(Box::new(Sleep::default())), + "sync" => Some(Box::new(SYnc::default())), "true" => Some(Box::new(True::default())), "which" => Some(Box::new(Which::default())), "whoami" => Some(Box::new(Whoami::default())), @@ -83,7 +84,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&'static str; 24] = [ +pub static COMMANDS: [&'static str; 25] = [ "base32", "base64", "basename", @@ -104,6 +105,7 @@ pub static COMMANDS: [&'static str; 24] = [ "rev", "sleep", "shitbox", + "sync", "true", "which", "whoami", diff --git a/src/cmd/sync/mod.rs b/src/cmd/sync/mod.rs index 8b13789..86fe70f 100644 --- a/src/cmd/sync/mod.rs +++ b/src/cmd/sync/mod.rs @@ -1 +1,77 @@ +use super::Cmd; +use clap::{Arg, ArgAction, Command}; +use std::{error::Error, io, ffi::CString}; +#[derive(Debug, Default)] +pub struct SYnc; + +impl Cmd for SYnc { + fn cli(&self) -> clap::Command { + Command::new("sync") + .about("force completion of pending disk writes (flush cache)") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("data") + .short('d') + .long("data") + .help("sync only file data, no unneeded metadata") + .conflicts_with("fs") + .action(ArgAction::SetTrue), + Arg::new("fs") + .short('f') + .long("file-system") + .help("sync the file systems that contain the files") + .action(ArgAction::SetTrue), + Arg::new("FILE") + .help( + "If one or more files are specified, sync only them, or \ + their containing file systems.", + ) + .num_args(0..), + ]) + } + + fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box> { + let Some(matches) = matches else { + return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input"))); + }; + if let Some(files) = matches.get_many::("FILE") { + for f in files { + let file = CString::new(f.as_bytes())?; + if matches.get_flag("data") { + let rc = unsafe { + let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME); + libc::fdatasync(fd) + }; + if rc != 0 { + return Err(Box::new(io::Error::last_os_error())); + } + } else if matches.get_flag("fs") { + let rc = unsafe { + let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME); + libc::syncfs(fd) + }; + if rc != 0 { + return Err(Box::new(io::Error::last_os_error())); + } + } else { + let rc = unsafe { + let fd = libc::open(file.as_ptr(), libc::O_RDWR | libc::O_NOATIME); + libc::fsync(fd) + }; + if rc != 0 { + return Err(Box::new(io::Error::last_os_error())); + } + } + } + } else { + unsafe { libc::sync(); } + } + Ok(()) + } + + fn path(&self) -> Option { + Some(crate::Path::Bin) + } +}