mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 20:43:41 +00:00
* refactor project structure * environment-based default registry * fix tests * update build container * use docker platform for iso build emulation * simplify compat * Fix docker platform spec in run-compat.sh * handle riscv compat * fix bug with dep error exists attr * undo removal of sorting * use qemu for iso stage --------- Co-authored-by: Mariusz Kogen <k0gen@pm.me> Co-authored-by: Matt Hill <mattnine@protonmail.com>
157 lines
4.9 KiB
Rust
157 lines
4.9 KiB
Rust
use std::ffi::OsStr;
|
|
use std::fmt::Display;
|
|
use std::os::unix::fs::MetadataExt;
|
|
use std::path::Path;
|
|
use std::str::FromStr;
|
|
|
|
use clap::Parser;
|
|
use clap::builder::ValueParserFactory;
|
|
use digest::generic_array::GenericArray;
|
|
use digest::{Digest, OutputSizeUser};
|
|
use serde::{Deserialize, Serialize};
|
|
use sha2::Sha256;
|
|
use tokio::process::Command;
|
|
use ts_rs::TS;
|
|
|
|
use super::FileSystem;
|
|
use crate::disk::mount::filesystem::MountType;
|
|
use crate::prelude::*;
|
|
use crate::util::{FromStrParser, Invoke};
|
|
|
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize, Parser, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct IdMap {
|
|
pub from_id: u32,
|
|
pub to_id: u32,
|
|
pub range: u32,
|
|
}
|
|
impl IdMap {
|
|
pub fn stack(a: Vec<IdMap>, b: Vec<IdMap>) -> Vec<IdMap> {
|
|
let mut res = Vec::with_capacity(a.len() + b.len());
|
|
res.extend_from_slice(&a);
|
|
|
|
for mut b in b {
|
|
for a in &a {
|
|
if a.from_id <= b.to_id && a.from_id + a.range > b.to_id {
|
|
b.to_id += a.to_id;
|
|
}
|
|
}
|
|
res.push(b);
|
|
}
|
|
|
|
res
|
|
}
|
|
}
|
|
impl FromStr for IdMap {
|
|
type Err = Error;
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let split = s.splitn(3, ":").collect::<Vec<_>>();
|
|
if let Some([u, k, r]) = split.get(0..3) {
|
|
Ok(Self {
|
|
from_id: u.parse()?,
|
|
to_id: k.parse()?,
|
|
range: r.parse()?,
|
|
})
|
|
} else if let Some([u, k]) = split.get(0..2) {
|
|
Ok(Self {
|
|
from_id: u.parse()?,
|
|
to_id: k.parse()?,
|
|
range: 1,
|
|
})
|
|
} else {
|
|
Err(Error::new(
|
|
eyre!("{s} is not a valid idmap"),
|
|
ErrorKind::ParseNumber,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
impl ValueParserFactory for IdMap {
|
|
type Parser = FromStrParser<IdMap>;
|
|
fn value_parser() -> Self::Parser {
|
|
<Self::Parser>::new()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct IdMapped<Fs: FileSystem> {
|
|
filesystem: Fs,
|
|
idmap: Vec<IdMap>,
|
|
}
|
|
impl<Fs: FileSystem> IdMapped<Fs> {
|
|
pub fn new(filesystem: Fs, idmap: Vec<IdMap>) -> Self {
|
|
Self { filesystem, idmap }
|
|
}
|
|
}
|
|
impl<Fs: FileSystem> FileSystem for IdMapped<Fs> {
|
|
fn mount_type(&self) -> Option<impl AsRef<str>> {
|
|
self.filesystem.mount_type()
|
|
}
|
|
fn extra_args(&self) -> impl IntoIterator<Item = impl AsRef<OsStr>> {
|
|
self.filesystem.extra_args()
|
|
}
|
|
fn mount_options(&self) -> impl IntoIterator<Item = impl Display> {
|
|
self.filesystem
|
|
.mount_options()
|
|
.into_iter()
|
|
.map(|a| Box::new(a) as Box<dyn Display>)
|
|
.chain(if self.idmap.is_empty() {
|
|
None
|
|
} else {
|
|
use std::fmt::Write;
|
|
|
|
let mut option = "X-mount.idmap=".to_owned();
|
|
for i in &self.idmap {
|
|
write!(&mut option, "b:{}:{}:{} ", i.from_id, i.to_id, i.range).unwrap();
|
|
}
|
|
Some(Box::new(option) as Box<dyn Display>)
|
|
})
|
|
}
|
|
async fn source(&self) -> Result<Option<impl AsRef<Path>>, Error> {
|
|
self.filesystem.source().await
|
|
}
|
|
async fn pre_mount(&self, mountpoint: &Path, mount_type: MountType) -> Result<(), Error> {
|
|
self.filesystem.pre_mount(mountpoint, mount_type).await?;
|
|
let info = tokio::fs::metadata(mountpoint).await?;
|
|
for i in &self.idmap {
|
|
let uid_in_range = i.from_id <= info.uid() && i.from_id + i.range > info.uid();
|
|
let gid_in_range = i.from_id <= info.gid() && i.from_id + i.range > info.gid();
|
|
if uid_in_range || gid_in_range {
|
|
Command::new("chown")
|
|
.arg(format!(
|
|
"{uid}:{gid}",
|
|
uid = if uid_in_range {
|
|
i.to_id + info.uid() - i.from_id
|
|
} else {
|
|
info.uid()
|
|
},
|
|
gid = if gid_in_range {
|
|
i.to_id + info.gid() - i.from_id
|
|
} else {
|
|
info.gid()
|
|
},
|
|
))
|
|
.arg(&mountpoint)
|
|
.invoke(crate::ErrorKind::Filesystem)
|
|
.await?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
async fn source_hash(
|
|
&self,
|
|
) -> Result<GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>, Error> {
|
|
let mut sha = Sha256::new();
|
|
sha.update("IdMapped");
|
|
sha.update(self.filesystem.source_hash().await?);
|
|
sha.update(usize::to_be_bytes(self.idmap.len()));
|
|
for i in &self.idmap {
|
|
sha.update(u32::to_be_bytes(i.from_id));
|
|
sha.update(u32::to_be_bytes(i.to_id));
|
|
sha.update(u32::to_be_bytes(i.range));
|
|
}
|
|
Ok(sha.finalize())
|
|
}
|
|
}
|