Files
start-os/core/src/disk/mount/filesystem/bind.rs

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