mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 04:01:58 +00:00
* permit IP for cifs backups * allow ip instead of hostname (#2347) --------- Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Co-authored-by: Aiden McClelland <me@drbonez.dev>
109 lines
2.9 KiB
Rust
109 lines
2.9 KiB
Rust
use std::net::IpAddr;
|
|
use std::os::unix::ffi::OsStrExt;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use async_trait::async_trait;
|
|
use digest::generic_array::GenericArray;
|
|
use digest::{Digest, OutputSizeUser};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Sha256;
|
|
use tokio::process::Command;
|
|
use tracing::instrument;
|
|
|
|
use super::{FileSystem, MountType, ReadOnly};
|
|
use crate::disk::mount::guard::TmpMountGuard;
|
|
use crate::util::Invoke;
|
|
use crate::Error;
|
|
|
|
async fn resolve_hostname(hostname: &str) -> Result<IpAddr, Error> {
|
|
if let Ok(addr) = hostname.parse() {
|
|
return Ok(addr);
|
|
}
|
|
#[cfg(feature = "avahi")]
|
|
if hostname.ends_with(".local") {
|
|
return Ok(IpAddr::V4(crate::net::mdns::resolve_mdns(hostname).await?));
|
|
}
|
|
Ok(String::from_utf8(
|
|
Command::new("nmblookup")
|
|
.arg(hostname)
|
|
.invoke(crate::ErrorKind::Network)
|
|
.await?,
|
|
)?
|
|
.split(" ")
|
|
.next()
|
|
.unwrap()
|
|
.trim()
|
|
.parse()?)
|
|
}
|
|
|
|
#[instrument(skip_all)]
|
|
pub async fn mount_cifs(
|
|
hostname: &str,
|
|
path: impl AsRef<Path>,
|
|
username: &str,
|
|
password: Option<&str>,
|
|
mountpoint: impl AsRef<Path>,
|
|
mount_type: MountType,
|
|
) -> Result<(), Error> {
|
|
tokio::fs::create_dir_all(mountpoint.as_ref()).await?;
|
|
let ip: IpAddr = resolve_hostname(hostname).await?;
|
|
let absolute_path = Path::new("/").join(path.as_ref());
|
|
let mut cmd = Command::new("mount");
|
|
cmd.arg("-t")
|
|
.arg("cifs")
|
|
.env("USER", username)
|
|
.env("PASSWD", password.unwrap_or_default())
|
|
.arg(format!("//{}{}", ip, absolute_path.display()))
|
|
.arg(mountpoint.as_ref());
|
|
if mount_type == ReadOnly {
|
|
cmd.arg("-o").arg("ro,noserverino");
|
|
} else {
|
|
cmd.arg("-o").arg("noserverino");
|
|
}
|
|
cmd.invoke(crate::ErrorKind::Filesystem).await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub struct Cifs {
|
|
pub hostname: String,
|
|
pub path: PathBuf,
|
|
pub username: String,
|
|
pub password: Option<String>,
|
|
}
|
|
impl Cifs {
|
|
pub async fn mountable(&self) -> Result<(), Error> {
|
|
let guard = TmpMountGuard::mount(self, ReadOnly).await?;
|
|
guard.unmount().await?;
|
|
Ok(())
|
|
}
|
|
}
|
|
#[async_trait]
|
|
impl FileSystem for Cifs {
|
|
async fn mount<P: AsRef<std::path::Path> + Send + Sync>(
|
|
&self,
|
|
mountpoint: P,
|
|
mount_type: MountType,
|
|
) -> Result<(), Error> {
|
|
mount_cifs(
|
|
&self.hostname,
|
|
&self.path,
|
|
&self.username,
|
|
self.password.as_ref().map(|p| p.as_str()),
|
|
mountpoint,
|
|
mount_type,
|
|
)
|
|
.await
|
|
}
|
|
async fn source_hash(
|
|
&self,
|
|
) -> Result<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>, Error> {
|
|
let mut sha = Sha256::new();
|
|
sha.update("Cifs");
|
|
sha.update(self.hostname.as_bytes());
|
|
sha.update(self.path.as_os_str().as_bytes());
|
|
Ok(sha.finalize())
|
|
}
|
|
}
|