mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 18:31:52 +00:00
107 lines
3.2 KiB
Rust
107 lines
3.2 KiB
Rust
use std::os::unix::ffi::OsStrExt;
|
|
use std::path::Path;
|
|
|
|
use digest::generic_array::GenericArray;
|
|
use digest::{Digest, OutputSizeUser};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Sha256;
|
|
use ts_rs::TS;
|
|
|
|
use super::FileSystem;
|
|
use crate::disk::mount::filesystem::MountType;
|
|
use crate::prelude::*;
|
|
use crate::util::io::create_file;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
#[ts(export)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum FileType {
|
|
File,
|
|
Directory,
|
|
Infer,
|
|
}
|
|
impl Default for FileType {
|
|
fn default() -> Self {
|
|
FileType::Directory
|
|
}
|
|
}
|
|
|
|
pub struct Bind<Src: AsRef<Path>> {
|
|
src: Src,
|
|
filetype: FileType,
|
|
recursive: bool,
|
|
}
|
|
impl<Src: AsRef<Path>> Bind<Src> {
|
|
pub fn new(src: Src) -> Self {
|
|
Self {
|
|
src,
|
|
filetype: FileType::Directory,
|
|
recursive: false,
|
|
}
|
|
}
|
|
pub fn with_type(mut self, filetype: FileType) -> Self {
|
|
self.filetype = filetype;
|
|
self
|
|
}
|
|
pub fn recursive(mut self, recursive: bool) -> Self {
|
|
self.recursive = recursive;
|
|
self
|
|
}
|
|
}
|
|
impl<Src: AsRef<Path> + Send + Sync> FileSystem for Bind<Src> {
|
|
async fn source(&self) -> Result<Option<impl AsRef<Path>>, Error> {
|
|
Ok(Some(&self.src))
|
|
}
|
|
fn extra_args(&self) -> impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>> {
|
|
[if self.recursive { "--rbind" } else { "--bind" }]
|
|
}
|
|
async fn pre_mount(&self, mountpoint: &Path, mount_type: MountType) -> Result<(), Error> {
|
|
let from_meta = tokio::fs::metadata(&self.src).await.ok();
|
|
let to_meta = tokio::fs::metadata(&mountpoint).await.ok();
|
|
if matches!(self.filetype, FileType::File)
|
|
|| (matches!(self.filetype, FileType::Infer)
|
|
&& from_meta.as_ref().map_or(false, |m| m.is_file()))
|
|
{
|
|
if to_meta.as_ref().map_or(false, |m| m.is_dir()) {
|
|
tokio::fs::remove_dir(mountpoint).await?;
|
|
}
|
|
if from_meta.is_none() && mount_type == MountType::ReadWrite {
|
|
create_file(self.src.as_ref()).await?.sync_all().await?;
|
|
}
|
|
if to_meta.is_none() {
|
|
create_file(mountpoint).await?.sync_all().await?;
|
|
}
|
|
} else {
|
|
if to_meta.as_ref().map_or(false, |m| m.is_file()) {
|
|
tokio::fs::remove_file(mountpoint).await?;
|
|
}
|
|
if from_meta.is_none() && mount_type == MountType::ReadWrite {
|
|
tokio::fs::create_dir_all(self.src.as_ref()).await?;
|
|
}
|
|
if to_meta.is_none() {
|
|
tokio::fs::create_dir_all(mountpoint).await?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
async fn source_hash(
|
|
&self,
|
|
) -> Result<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>, Error> {
|
|
let mut sha = Sha256::new();
|
|
sha.update("Bind");
|
|
sha.update(
|
|
tokio::fs::canonicalize(self.src.as_ref())
|
|
.await
|
|
.with_ctx(|_| {
|
|
(
|
|
crate::ErrorKind::Filesystem,
|
|
self.src.as_ref().display().to_string(),
|
|
)
|
|
})?
|
|
.as_os_str()
|
|
.as_bytes(),
|
|
);
|
|
Ok(sha.finalize())
|
|
}
|
|
}
|