Files
start-os/backend/src/disk/mount/filesystem/cifs.rs
Matt Hill 36c3617204 permit IP for cifs backups (#2342)
* 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>
2023-07-14 18:52:33 +00:00

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())
}
}