use std::fs; use std::io; use std::os::unix::fs::MetadataExt; use std::os::unix::fs::PermissionsExt; use std::os::unix::net::UnixStream; use std::path::Path; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) struct SocketFileIdentity { dev: u64, ino: u64, } pub(crate) fn prepare_socket_path( path: &Path, busy_message: impl FnOnce(&Path) -> String, ) -> io::Result<()> { if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } if !path.exists() { return Ok(()); } match UnixStream::connect(path) { Ok(_) => { return Err(io::Error::new(io::ErrorKind::AddrInUse, busy_message(path))); } Err(err) if matches!( err.kind(), io::ErrorKind::ConnectionRefused | io::ErrorKind::NotFound | io::ErrorKind::TimedOut ) => {} Err(err) => return Err(err), } if let Err(err) = fs::remove_file(path) { if err.kind() != io::ErrorKind::NotFound { return Err(err); } } Ok(()) } pub(crate) fn socket_file_identity(path: &Path) -> io::Result { let metadata = fs::metadata(path)?; Ok(SocketFileIdentity { dev: metadata.dev(), ino: metadata.ino(), }) } pub(crate) fn remove_socket_file_if_owned( path: &Path, identity: SocketFileIdentity, ) -> io::Result<()> { let current = match socket_file_identity(path) { Ok(current) => current, Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), Err(err) => return Err(err), }; if current != identity { return Ok(()); } match fs::remove_file(path) { Ok(()) => Ok(()), Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), Err(err) => Err(err), } } pub(crate) fn restrict_socket_permissions(path: &Path, mode: u32) -> io::Result<()> { let mut permissions = fs::metadata(path)?.permissions(); permissions.set_mode(mode); fs::set_permissions(path, permissions) }